Fix #6673 - mobile cards issues
[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 };
675
676 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
677     
678     tag: 'div',
679     cls: '',
680     html: '',
681     preventDefault: false, 
682     clickable: false,
683     tapedTwice : false,
684     
685     getAutoCreate : function(){
686         
687         var cfg = {
688             tag: this.tag,
689             // cls: this.cls, double assign in parent class Component.js :: onRender
690             html: this.html
691         };
692         
693         return cfg;
694     },
695     
696     initEvents: function() 
697     {
698         Roo.bootstrap.Element.superclass.initEvents.call(this);
699         
700         if(this.clickable){
701             this.el.on('click', this.onClick, this);
702         }
703         
704         
705     },
706     
707     onClick : function(e)
708     {
709         if(this.preventDefault){
710             e.preventDefault();
711         }
712         
713         this.fireEvent('dblclick', this, e);
714     },
715     
716     
717     
718
719     
720     
721     getValue : function()
722     {
723         return this.el.dom.innerHTML;
724     },
725     
726     setValue : function(value)
727     {
728         this.el.dom.innerHTML = value;
729     }
730    
731 });
732
733  
734
735  /*
736  * - LGPL
737  *
738  * dropable area
739  * 
740  */
741
742 /**
743  * @class Roo.bootstrap.DropTarget
744  * @extends Roo.bootstrap.Element
745  * Bootstrap DropTarget class
746  
747  * @cfg {string} name dropable name
748  * 
749  * @constructor
750  * Create a new Dropable Area
751  * @param {Object} config The config object
752  */
753
754 Roo.bootstrap.DropTarget = function(config){
755     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
756     
757     this.addEvents({
758         // raw events
759         /**
760          * @event click
761          * When a element is chick
762          * @param {Roo.bootstrap.Element} this
763          * @param {Roo.EventObject} e
764          */
765         "drop" : true
766     });
767 };
768
769 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
770     
771     
772     getAutoCreate : function(){
773         
774          
775     },
776     
777     initEvents: function() 
778     {
779         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
780         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
781             ddGroup: this.name,
782             listeners : {
783                 drop : this.dragDrop.createDelegate(this),
784                 enter : this.dragEnter.createDelegate(this),
785                 out : this.dragOut.createDelegate(this),
786                 over : this.dragOver.createDelegate(this)
787             }
788             
789         });
790         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
791     },
792     
793     dragDrop : function(source,e,data)
794     {
795         // user has to decide how to impliment this.
796         Roo.log('drop');
797         Roo.log(this);
798         //this.fireEvent('drop', this, source, e ,data);
799         return false;
800     },
801     
802     dragEnter : function(n, dd, e, data)
803     {
804         // probably want to resize the element to match the dropped element..
805         Roo.log("enter");
806         this.originalSize = this.el.getSize();
807         this.el.setSize( n.el.getSize());
808         this.dropZone.DDM.refreshCache(this.name);
809         Roo.log([n, dd, e, data]);
810     },
811     
812     dragOut : function(value)
813     {
814         // resize back to normal
815         Roo.log("out");
816         this.el.setSize(this.originalSize);
817         this.dropZone.resetConstraints();
818     },
819     
820     dragOver : function()
821     {
822         // ??? do nothing?
823     }
824    
825 });
826
827  
828
829  /*
830  * - LGPL
831  *
832  * Body
833  *
834  */
835
836 /**
837  * @class Roo.bootstrap.Body
838  * @extends Roo.bootstrap.Component
839  * Bootstrap Body class
840  *
841  * @constructor
842  * Create a new body
843  * @param {Object} config The config object
844  */
845
846 Roo.bootstrap.Body = function(config){
847
848     config = config || {};
849
850     Roo.bootstrap.Body.superclass.constructor.call(this, config);
851     this.el = Roo.get(config.el ? config.el : document.body );
852     if (this.cls && this.cls.length) {
853         Roo.get(document.body).addClass(this.cls);
854     }
855 };
856
857 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
858
859     is_body : true,// just to make sure it's constructed?
860
861         autoCreate : {
862         cls: 'container'
863     },
864     onRender : function(ct, position)
865     {
866        /* Roo.log("Roo.bootstrap.Body - onRender");
867         if (this.cls && this.cls.length) {
868             Roo.get(document.body).addClass(this.cls);
869         }
870         // style??? xttr???
871         */
872     }
873
874
875
876
877 });
878 /*
879  * - LGPL
880  *
881  * button group
882  * 
883  */
884
885
886 /**
887  * @class Roo.bootstrap.ButtonGroup
888  * @extends Roo.bootstrap.Component
889  * Bootstrap ButtonGroup class
890  * @cfg {String} size lg | sm | xs (default empty normal)
891  * @cfg {String} align vertical | justified  (default none)
892  * @cfg {String} direction up | down (default down)
893  * @cfg {Boolean} toolbar false | true
894  * @cfg {Boolean} btn true | false
895  * 
896  * 
897  * @constructor
898  * Create a new Input
899  * @param {Object} config The config object
900  */
901
902 Roo.bootstrap.ButtonGroup = function(config){
903     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
904 };
905
906 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
907     
908     size: '',
909     align: '',
910     direction: '',
911     toolbar: false,
912     btn: true,
913
914     getAutoCreate : function(){
915         var cfg = {
916             cls: 'btn-group',
917             html : null
918         };
919         
920         cfg.html = this.html || cfg.html;
921         
922         if (this.toolbar) {
923             cfg = {
924                 cls: 'btn-toolbar',
925                 html: null
926             };
927             
928             return cfg;
929         }
930         
931         if (['vertical','justified'].indexOf(this.align)!==-1) {
932             cfg.cls = 'btn-group-' + this.align;
933             
934             if (this.align == 'justified') {
935                 console.log(this.items);
936             }
937         }
938         
939         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
940             cfg.cls += ' btn-group-' + this.size;
941         }
942         
943         if (this.direction == 'up') {
944             cfg.cls += ' dropup' ;
945         }
946         
947         return cfg;
948     },
949     /**
950      * Add a button to the group (similar to NavItem API.)
951      */
952     addItem : function(cfg)
953     {
954         var cn = new Roo.bootstrap.Button(cfg);
955         //this.register(cn);
956         cn.parentId = this.id;
957         cn.onRender(this.el, null);
958         return cn;
959     }
960    
961 });
962
963  /*
964  * - LGPL
965  *
966  * button
967  * 
968  */
969
970 /**
971  * @class Roo.bootstrap.Button
972  * @extends Roo.bootstrap.Component
973  * Bootstrap Button class
974  * @cfg {String} html The button content
975  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
976  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
977  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
978  * @cfg {String} size (lg|sm|xs)
979  * @cfg {String} tag (a|input|submit)
980  * @cfg {String} href empty or href
981  * @cfg {Boolean} disabled default false;
982  * @cfg {Boolean} isClose default false;
983  * @cfg {String} glyphicon depricated - use fa
984  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
985  * @cfg {String} badge text for badge
986  * @cfg {String} theme (default|glow)  
987  * @cfg {Boolean} inverse dark themed version
988  * @cfg {Boolean} toggle is it a slidy toggle button
989  * @cfg {Boolean} pressed   default null - if the button ahs active state
990  * @cfg {String} ontext text for on slidy toggle state
991  * @cfg {String} offtext text for off slidy toggle state
992  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
993  * @cfg {Boolean} removeClass remove the standard class..
994  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
995  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
996  * 
997  * @constructor
998  * Create a new button
999  * @param {Object} config The config object
1000  */
1001
1002
1003 Roo.bootstrap.Button = function(config){
1004     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1005     
1006     this.addEvents({
1007         // raw events
1008         /**
1009          * @event click
1010          * When a button is pressed
1011          * @param {Roo.bootstrap.Button} btn
1012          * @param {Roo.EventObject} e
1013          */
1014         "click" : true,
1015         /**
1016          * @event dblclick
1017          * When a button is double clicked
1018          * @param {Roo.bootstrap.Button} btn
1019          * @param {Roo.EventObject} e
1020          */
1021         "dblclick" : true,
1022          /**
1023          * @event toggle
1024          * After the button has been toggles
1025          * @param {Roo.bootstrap.Button} btn
1026          * @param {Roo.EventObject} e
1027          * @param {boolean} pressed (also available as button.pressed)
1028          */
1029         "toggle" : true
1030     });
1031 };
1032
1033 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1034     html: false,
1035     active: false,
1036     weight: '',
1037     badge_weight: '',
1038     outline : false,
1039     size: '',
1040     tag: 'button',
1041     href: '',
1042     disabled: false,
1043     isClose: false,
1044     glyphicon: '',
1045     fa: '',
1046     badge: '',
1047     theme: 'default',
1048     inverse: false,
1049     
1050     toggle: false,
1051     ontext: 'ON',
1052     offtext: 'OFF',
1053     defaulton: true,
1054     preventDefault: true,
1055     removeClass: false,
1056     name: false,
1057     target: false,
1058     group : false,
1059      
1060     pressed : null,
1061      
1062     
1063     getAutoCreate : function(){
1064         
1065         var cfg = {
1066             tag : 'button',
1067             cls : 'roo-button',
1068             html: ''
1069         };
1070         
1071         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1072             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1073             this.tag = 'button';
1074         } else {
1075             cfg.tag = this.tag;
1076         }
1077         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1078         
1079         if (this.toggle == true) {
1080             cfg={
1081                 tag: 'div',
1082                 cls: 'slider-frame roo-button',
1083                 cn: [
1084                     {
1085                         tag: 'span',
1086                         'data-on-text':'ON',
1087                         'data-off-text':'OFF',
1088                         cls: 'slider-button',
1089                         html: this.offtext
1090                     }
1091                 ]
1092             };
1093             // why are we validating the weights?
1094             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1095                 cfg.cls +=  ' ' + this.weight;
1096             }
1097             
1098             return cfg;
1099         }
1100         
1101         if (this.isClose) {
1102             cfg.cls += ' close';
1103             
1104             cfg["aria-hidden"] = true;
1105             
1106             cfg.html = "&times;";
1107             
1108             return cfg;
1109         }
1110              
1111         
1112         if (this.theme==='default') {
1113             cfg.cls = 'btn roo-button';
1114             
1115             //if (this.parentType != 'Navbar') {
1116             this.weight = this.weight.length ?  this.weight : 'default';
1117             //}
1118             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119                 
1120                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1121                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1122                 cfg.cls += ' btn-' + outline + weight;
1123                 if (this.weight == 'default') {
1124                     // BC
1125                     cfg.cls += ' btn-' + this.weight;
1126                 }
1127             }
1128         } else if (this.theme==='glow') {
1129             
1130             cfg.tag = 'a';
1131             cfg.cls = 'btn-glow roo-button';
1132             
1133             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1134                 
1135                 cfg.cls += ' ' + this.weight;
1136             }
1137         }
1138    
1139         
1140         if (this.inverse) {
1141             this.cls += ' inverse';
1142         }
1143         
1144         
1145         if (this.active || this.pressed === true) {
1146             cfg.cls += ' active';
1147         }
1148         
1149         if (this.disabled) {
1150             cfg.disabled = 'disabled';
1151         }
1152         
1153         if (this.items) {
1154             Roo.log('changing to ul' );
1155             cfg.tag = 'ul';
1156             this.glyphicon = 'caret';
1157             if (Roo.bootstrap.version == 4) {
1158                 this.fa = 'caret-down';
1159             }
1160             
1161         }
1162         
1163         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1164          
1165         //gsRoo.log(this.parentType);
1166         if (this.parentType === 'Navbar' && !this.parent().bar) {
1167             Roo.log('changing to li?');
1168             
1169             cfg.tag = 'li';
1170             
1171             cfg.cls = '';
1172             cfg.cn =  [{
1173                 tag : 'a',
1174                 cls : 'roo-button',
1175                 html : this.html,
1176                 href : this.href || '#'
1177             }];
1178             if (this.menu) {
1179                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1180                 cfg.cls += ' dropdown';
1181             }   
1182             
1183             delete cfg.html;
1184             
1185         }
1186         
1187        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1188         
1189         if (this.glyphicon) {
1190             cfg.html = ' ' + cfg.html;
1191             
1192             cfg.cn = [
1193                 {
1194                     tag: 'span',
1195                     cls: 'glyphicon glyphicon-' + this.glyphicon
1196                 }
1197             ];
1198         }
1199         if (this.fa) {
1200             cfg.html = ' ' + cfg.html;
1201             
1202             cfg.cn = [
1203                 {
1204                     tag: 'i',
1205                     cls: 'fa fas fa-' + this.fa
1206                 }
1207             ];
1208         }
1209         
1210         if (this.badge) {
1211             cfg.html += ' ';
1212             
1213             cfg.tag = 'a';
1214             
1215 //            cfg.cls='btn roo-button';
1216             
1217             cfg.href=this.href;
1218             
1219             var value = cfg.html;
1220             
1221             if(this.glyphicon){
1222                 value = {
1223                     tag: 'span',
1224                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1225                     html: this.html
1226                 };
1227             }
1228             if(this.fa){
1229                 value = {
1230                     tag: 'i',
1231                     cls: 'fa fas fa-' + this.fa,
1232                     html: this.html
1233                 };
1234             }
1235             
1236             var bw = this.badge_weight.length ? this.badge_weight :
1237                 (this.weight.length ? this.weight : 'secondary');
1238             bw = bw == 'default' ? 'secondary' : bw;
1239             
1240             cfg.cn = [
1241                 value,
1242                 {
1243                     tag: 'span',
1244                     cls: 'badge badge-' + bw,
1245                     html: this.badge
1246                 }
1247             ];
1248             
1249             cfg.html='';
1250         }
1251         
1252         if (this.menu) {
1253             cfg.cls += ' dropdown';
1254             cfg.html = typeof(cfg.html) != 'undefined' ?
1255                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1256         }
1257         
1258         if (cfg.tag !== 'a' && this.href !== '') {
1259             throw "Tag must be a to set href.";
1260         } else if (this.href.length > 0) {
1261             cfg.href = this.href;
1262         }
1263         
1264         if(this.removeClass){
1265             cfg.cls = '';
1266         }
1267         
1268         if(this.target){
1269             cfg.target = this.target;
1270         }
1271         
1272         return cfg;
1273     },
1274     initEvents: function() {
1275        // Roo.log('init events?');
1276 //        Roo.log(this.el.dom);
1277         // add the menu...
1278         
1279         if (typeof (this.menu) != 'undefined') {
1280             this.menu.parentType = this.xtype;
1281             this.menu.triggerEl = this.el;
1282             this.addxtype(Roo.apply({}, this.menu));
1283         }
1284
1285
1286         if (this.el.hasClass('roo-button')) {
1287              this.el.on('click', this.onClick, this);
1288              this.el.on('dblclick', this.onDblClick, this);
1289         } else {
1290              this.el.select('.roo-button').on('click', this.onClick, this);
1291              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1292              
1293         }
1294         // why?
1295         if(this.removeClass){
1296             this.el.on('click', this.onClick, this);
1297         }
1298         
1299         if (this.group === true) {
1300              if (this.pressed === false || this.pressed === true) {
1301                 // nothing
1302             } else {
1303                 this.pressed = false;
1304                 this.setActive(this.pressed);
1305             }
1306             
1307         }
1308         
1309         this.el.enableDisplayMode();
1310         
1311     },
1312     onClick : function(e)
1313     {
1314         if (this.disabled) {
1315             return;
1316         }
1317         
1318         Roo.log('button on click ');
1319         if(this.preventDefault){
1320             e.preventDefault();
1321         }
1322         
1323         if (this.group) {
1324             if (this.pressed) {
1325                 // do nothing -
1326                 return;
1327             }
1328             this.setActive(true);
1329             var pi = this.parent().items;
1330             for (var i = 0;i < pi.length;i++) {
1331                 if (this == pi[i]) {
1332                     continue;
1333                 }
1334                 if (pi[i].el.hasClass('roo-button')) {
1335                     pi[i].setActive(false);
1336                 }
1337             }
1338             this.fireEvent('click', this, e);            
1339             return;
1340         }
1341         
1342         if (this.pressed === true || this.pressed === false) {
1343             this.toggleActive(e);
1344         }
1345         
1346         
1347         this.fireEvent('click', this, e);
1348     },
1349     onDblClick: function(e)
1350     {
1351         if (this.disabled) {
1352             return;
1353         }
1354         if(this.preventDefault){
1355             e.preventDefault();
1356         }
1357         this.fireEvent('dblclick', this, e);
1358     },
1359     /**
1360      * Enables this button
1361      */
1362     enable : function()
1363     {
1364         this.disabled = false;
1365         this.el.removeClass('disabled');
1366         this.el.dom.removeAttribute("disabled");
1367     },
1368     
1369     /**
1370      * Disable this button
1371      */
1372     disable : function()
1373     {
1374         this.disabled = true;
1375         this.el.addClass('disabled');
1376         this.el.attr("disabled", "disabled")
1377     },
1378      /**
1379      * sets the active state on/off, 
1380      * @param {Boolean} state (optional) Force a particular state
1381      */
1382     setActive : function(v) {
1383         
1384         this.el[v ? 'addClass' : 'removeClass']('active');
1385         this.pressed = v;
1386     },
1387      /**
1388      * toggles the current active state 
1389      */
1390     toggleActive : function(e)
1391     {
1392         this.setActive(!this.pressed); // this modifies pressed...
1393         this.fireEvent('toggle', this, e, this.pressed);
1394     },
1395      /**
1396      * get the current active state
1397      * @return {boolean} true if it's active
1398      */
1399     isActive : function()
1400     {
1401         return this.el.hasClass('active');
1402     },
1403     /**
1404      * set the text of the first selected button
1405      */
1406     setText : function(str)
1407     {
1408         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1409     },
1410     /**
1411      * get the text of the first selected button
1412      */
1413     getText : function()
1414     {
1415         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1416     },
1417     
1418     setWeight : function(str)
1419     {
1420         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1421         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1422         this.weight = str;
1423         var outline = this.outline ? 'outline-' : '';
1424         if (str == 'default') {
1425             this.el.addClass('btn-default btn-outline-secondary');        
1426             return;
1427         }
1428         this.el.addClass('btn-' + outline + str);        
1429     }
1430     
1431     
1432 });
1433 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1434
1435 Roo.bootstrap.Button.weights = [
1436     'default',
1437     'secondary' ,
1438     'primary',
1439     'success',
1440     'info',
1441     'warning',
1442     'danger',
1443     'link',
1444     'light',
1445     'dark'              
1446    
1447 ];/*
1448  * - LGPL
1449  *
1450  * column
1451  * 
1452  */
1453
1454 /**
1455  * @class Roo.bootstrap.Column
1456  * @extends Roo.bootstrap.Component
1457  * Bootstrap Column class
1458  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1459  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1460  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1461  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1462  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1463  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1464  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1465  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1466  *
1467  * 
1468  * @cfg {Boolean} hidden (true|false) hide the element
1469  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1470  * @cfg {String} fa (ban|check|...) font awesome icon
1471  * @cfg {Number} fasize (1|2|....) font awsome size
1472
1473  * @cfg {String} icon (info-sign|check|...) glyphicon name
1474
1475  * @cfg {String} html content of column.
1476  * 
1477  * @constructor
1478  * Create a new Column
1479  * @param {Object} config The config object
1480  */
1481
1482 Roo.bootstrap.Column = function(config){
1483     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1484 };
1485
1486 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1487     
1488     xs: false,
1489     sm: false,
1490     md: false,
1491     lg: false,
1492     xsoff: false,
1493     smoff: false,
1494     mdoff: false,
1495     lgoff: false,
1496     html: '',
1497     offset: 0,
1498     alert: false,
1499     fa: false,
1500     icon : false,
1501     hidden : false,
1502     fasize : 1,
1503     
1504     getAutoCreate : function(){
1505         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1506         
1507         cfg = {
1508             tag: 'div',
1509             cls: 'column'
1510         };
1511         
1512         var settings=this;
1513         var sizes =   ['xs','sm','md','lg'];
1514         sizes.map(function(size ,ix){
1515             //Roo.log( size + ':' + settings[size]);
1516             
1517             if (settings[size+'off'] !== false) {
1518                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1519             }
1520             
1521             if (settings[size] === false) {
1522                 return;
1523             }
1524             
1525             if (!settings[size]) { // 0 = hidden
1526                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1527                 // bootsrap4
1528                 for (var i = ix; i > -1; i--) {
1529                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1530                 }
1531                 
1532                 
1533                 return;
1534             }
1535             cfg.cls += ' col-' + size + '-' + settings[size] + (
1536                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1537             );
1538             
1539         });
1540         
1541         if (this.hidden) {
1542             cfg.cls += ' hidden';
1543         }
1544         
1545         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1546             cfg.cls +=' alert alert-' + this.alert;
1547         }
1548         
1549         
1550         if (this.html.length) {
1551             cfg.html = this.html;
1552         }
1553         if (this.fa) {
1554             var fasize = '';
1555             if (this.fasize > 1) {
1556                 fasize = ' fa-' + this.fasize + 'x';
1557             }
1558             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1559             
1560             
1561         }
1562         if (this.icon) {
1563             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1564         }
1565         
1566         return cfg;
1567     }
1568    
1569 });
1570
1571  
1572
1573  /*
1574  * - LGPL
1575  *
1576  * page container.
1577  * 
1578  */
1579
1580
1581 /**
1582  * @class Roo.bootstrap.Container
1583  * @extends Roo.bootstrap.Component
1584  * Bootstrap Container class
1585  * @cfg {Boolean} jumbotron is it a jumbotron element
1586  * @cfg {String} html content of element
1587  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1588  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1589  * @cfg {String} header content of header (for panel)
1590  * @cfg {String} footer content of footer (for panel)
1591  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1592  * @cfg {String} tag (header|aside|section) type of HTML tag.
1593  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1594  * @cfg {String} fa font awesome icon
1595  * @cfg {String} icon (info-sign|check|...) glyphicon name
1596  * @cfg {Boolean} hidden (true|false) hide the element
1597  * @cfg {Boolean} expandable (true|false) default false
1598  * @cfg {Boolean} expanded (true|false) default true
1599  * @cfg {String} rheader contet on the right of header
1600  * @cfg {Boolean} clickable (true|false) default false
1601
1602  *     
1603  * @constructor
1604  * Create a new Container
1605  * @param {Object} config The config object
1606  */
1607
1608 Roo.bootstrap.Container = function(config){
1609     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1610     
1611     this.addEvents({
1612         // raw events
1613          /**
1614          * @event expand
1615          * After the panel has been expand
1616          * 
1617          * @param {Roo.bootstrap.Container} this
1618          */
1619         "expand" : true,
1620         /**
1621          * @event collapse
1622          * After the panel has been collapsed
1623          * 
1624          * @param {Roo.bootstrap.Container} this
1625          */
1626         "collapse" : true,
1627         /**
1628          * @event click
1629          * When a element is chick
1630          * @param {Roo.bootstrap.Container} this
1631          * @param {Roo.EventObject} e
1632          */
1633         "click" : true
1634     });
1635 };
1636
1637 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1638     
1639     jumbotron : false,
1640     well: '',
1641     panel : '',
1642     header: '',
1643     footer : '',
1644     sticky: '',
1645     tag : false,
1646     alert : false,
1647     fa: false,
1648     icon : false,
1649     expandable : false,
1650     rheader : '',
1651     expanded : true,
1652     clickable: false,
1653   
1654      
1655     getChildContainer : function() {
1656         
1657         if(!this.el){
1658             return false;
1659         }
1660         
1661         if (this.panel.length) {
1662             return this.el.select('.panel-body',true).first();
1663         }
1664         
1665         return this.el;
1666     },
1667     
1668     
1669     getAutoCreate : function(){
1670         
1671         var cfg = {
1672             tag : this.tag || 'div',
1673             html : '',
1674             cls : ''
1675         };
1676         if (this.jumbotron) {
1677             cfg.cls = 'jumbotron';
1678         }
1679         
1680         
1681         
1682         // - this is applied by the parent..
1683         //if (this.cls) {
1684         //    cfg.cls = this.cls + '';
1685         //}
1686         
1687         if (this.sticky.length) {
1688             
1689             var bd = Roo.get(document.body);
1690             if (!bd.hasClass('bootstrap-sticky')) {
1691                 bd.addClass('bootstrap-sticky');
1692                 Roo.select('html',true).setStyle('height', '100%');
1693             }
1694              
1695             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1696         }
1697         
1698         
1699         if (this.well.length) {
1700             switch (this.well) {
1701                 case 'lg':
1702                 case 'sm':
1703                     cfg.cls +=' well well-' +this.well;
1704                     break;
1705                 default:
1706                     cfg.cls +=' well';
1707                     break;
1708             }
1709         }
1710         
1711         if (this.hidden) {
1712             cfg.cls += ' hidden';
1713         }
1714         
1715         
1716         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1717             cfg.cls +=' alert alert-' + this.alert;
1718         }
1719         
1720         var body = cfg;
1721         
1722         if (this.panel.length) {
1723             cfg.cls += ' panel panel-' + this.panel;
1724             cfg.cn = [];
1725             if (this.header.length) {
1726                 
1727                 var h = [];
1728                 
1729                 if(this.expandable){
1730                     
1731                     cfg.cls = cfg.cls + ' expandable';
1732                     
1733                     h.push({
1734                         tag: 'i',
1735                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1736                     });
1737                     
1738                 }
1739                 
1740                 h.push(
1741                     {
1742                         tag: 'span',
1743                         cls : 'panel-title',
1744                         html : (this.expandable ? '&nbsp;' : '') + this.header
1745                     },
1746                     {
1747                         tag: 'span',
1748                         cls: 'panel-header-right',
1749                         html: this.rheader
1750                     }
1751                 );
1752                 
1753                 cfg.cn.push({
1754                     cls : 'panel-heading',
1755                     style : this.expandable ? 'cursor: pointer' : '',
1756                     cn : h
1757                 });
1758                 
1759             }
1760             
1761             body = false;
1762             cfg.cn.push({
1763                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1764                 html : this.html
1765             });
1766             
1767             
1768             if (this.footer.length) {
1769                 cfg.cn.push({
1770                     cls : 'panel-footer',
1771                     html : this.footer
1772                     
1773                 });
1774             }
1775             
1776         }
1777         
1778         if (body) {
1779             body.html = this.html || cfg.html;
1780             // prefix with the icons..
1781             if (this.fa) {
1782                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1783             }
1784             if (this.icon) {
1785                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1786             }
1787             
1788             
1789         }
1790         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1791             cfg.cls =  'container';
1792         }
1793         
1794         return cfg;
1795     },
1796     
1797     initEvents: function() 
1798     {
1799         if(this.expandable){
1800             var headerEl = this.headerEl();
1801         
1802             if(headerEl){
1803                 headerEl.on('click', this.onToggleClick, this);
1804             }
1805         }
1806         
1807         if(this.clickable){
1808             this.el.on('click', this.onClick, this);
1809         }
1810         
1811     },
1812     
1813     onToggleClick : function()
1814     {
1815         var headerEl = this.headerEl();
1816         
1817         if(!headerEl){
1818             return;
1819         }
1820         
1821         if(this.expanded){
1822             this.collapse();
1823             return;
1824         }
1825         
1826         this.expand();
1827     },
1828     
1829     expand : function()
1830     {
1831         if(this.fireEvent('expand', this)) {
1832             
1833             this.expanded = true;
1834             
1835             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1836             
1837             this.el.select('.panel-body',true).first().removeClass('hide');
1838             
1839             var toggleEl = this.toggleEl();
1840
1841             if(!toggleEl){
1842                 return;
1843             }
1844
1845             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1846         }
1847         
1848     },
1849     
1850     collapse : function()
1851     {
1852         if(this.fireEvent('collapse', this)) {
1853             
1854             this.expanded = false;
1855             
1856             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1857             this.el.select('.panel-body',true).first().addClass('hide');
1858         
1859             var toggleEl = this.toggleEl();
1860
1861             if(!toggleEl){
1862                 return;
1863             }
1864
1865             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1866         }
1867     },
1868     
1869     toggleEl : function()
1870     {
1871         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1872             return;
1873         }
1874         
1875         return this.el.select('.panel-heading .fa',true).first();
1876     },
1877     
1878     headerEl : function()
1879     {
1880         if(!this.el || !this.panel.length || !this.header.length){
1881             return;
1882         }
1883         
1884         return this.el.select('.panel-heading',true).first()
1885     },
1886     
1887     bodyEl : function()
1888     {
1889         if(!this.el || !this.panel.length){
1890             return;
1891         }
1892         
1893         return this.el.select('.panel-body',true).first()
1894     },
1895     
1896     titleEl : function()
1897     {
1898         if(!this.el || !this.panel.length || !this.header.length){
1899             return;
1900         }
1901         
1902         return this.el.select('.panel-title',true).first();
1903     },
1904     
1905     setTitle : function(v)
1906     {
1907         var titleEl = this.titleEl();
1908         
1909         if(!titleEl){
1910             return;
1911         }
1912         
1913         titleEl.dom.innerHTML = v;
1914     },
1915     
1916     getTitle : function()
1917     {
1918         
1919         var titleEl = this.titleEl();
1920         
1921         if(!titleEl){
1922             return '';
1923         }
1924         
1925         return titleEl.dom.innerHTML;
1926     },
1927     
1928     setRightTitle : function(v)
1929     {
1930         var t = this.el.select('.panel-header-right',true).first();
1931         
1932         if(!t){
1933             return;
1934         }
1935         
1936         t.dom.innerHTML = v;
1937     },
1938     
1939     onClick : function(e)
1940     {
1941         e.preventDefault();
1942         
1943         this.fireEvent('click', this, e);
1944     }
1945 });
1946
1947  /*
1948  *  - LGPL
1949  *
1950  *  This is BS4's Card element.. - similar to our containers probably..
1951  * 
1952  */
1953 /**
1954  * @class Roo.bootstrap.Card
1955  * @extends Roo.bootstrap.Component
1956  * Bootstrap Card class
1957  *
1958  *
1959  * possible... may not be implemented..
1960  * @cfg {String} header_image  src url of image.
1961  * @cfg {String|Object} header
1962  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1963  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1964  * 
1965  * @cfg {String} title
1966  * @cfg {String} subtitle
1967  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1968  * @cfg {String} footer
1969  
1970  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1971  * 
1972  * @cfg {String} margin (0|1|2|3|4|5|auto)
1973  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1974  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1975  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1976  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1977  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1978  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1979  *
1980  * @cfg {String} padding (0|1|2|3|4|5)
1981  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1982  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1983  * @cfg {String} padding_left (0|1|2|3|4|5)
1984  * @cfg {String} padding_right (0|1|2|3|4|5)
1985  * @cfg {String} padding_x (0|1|2|3|4|5)
1986  * @cfg {String} padding_y (0|1|2|3|4|5)
1987  *
1988  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1989  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1990  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1991  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1992  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1993  
1994  * @config {Boolean} dragable  if this card can be dragged.
1995  * @config {String} drag_group  group for drag
1996  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1997  * @config {String} drop_group  group for drag
1998  * 
1999  * @config {Boolean} collapsable can the body be collapsed.
2000  * @config {Boolean} collapsed is the body collapsed when rendered...
2001  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2002  * @config {Boolean} rotated is the body rotated when rendered...
2003  * 
2004  * @constructor
2005  * Create a new Container
2006  * @param {Object} config The config object
2007  */
2008
2009 Roo.bootstrap.Card = function(config){
2010     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2011     
2012     this.addEvents({
2013          // raw events
2014         /**
2015          * @event drop
2016          * When a element a card is dropped
2017          * @param {Roo.bootstrap.Card} this
2018          *
2019          * 
2020          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2021          * @param {String} position 'above' or 'below'
2022          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2023         
2024          */
2025         'drop' : true,
2026          /**
2027          * @event rotate
2028          * When a element a card is rotate
2029          * @param {Roo.bootstrap.Card} this
2030          * @param {Roo.Element} n the node being dropped?
2031          * @param {Boolean} rotate status
2032          */
2033         'rotate' : true,
2034         /**
2035          * @event cardover
2036          * When a card element is dragged over ready to drop (return false to block dropable)
2037          * @param {Roo.bootstrap.Card} this
2038          * @param {Object} data from dragdrop 
2039          */
2040          'cardover' : true
2041          
2042     });
2043 };
2044
2045
2046 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2047     
2048     
2049     weight : '',
2050     
2051     margin: '', /// may be better in component?
2052     margin_top: '', 
2053     margin_bottom: '', 
2054     margin_left: '',
2055     margin_right: '',
2056     margin_x: '',
2057     margin_y: '',
2058     
2059     padding : '',
2060     padding_top: '', 
2061     padding_bottom: '', 
2062     padding_left: '',
2063     padding_right: '',
2064     padding_x: '',
2065     padding_y: '',
2066     
2067     display: '', 
2068     display_xs: '', 
2069     display_sm: '', 
2070     display_lg: '',
2071     display_xl: '',
2072  
2073     header_image  : '',
2074     header : '',
2075     header_size : 0,
2076     title : '',
2077     subtitle : '',
2078     html : '',
2079     footer: '',
2080
2081     collapsable : false,
2082     collapsed : false,
2083     rotateable : false,
2084     rotated : false,
2085     
2086     dragable : false,
2087     drag_group : false,
2088     dropable : false,
2089     drop_group : false,
2090     childContainer : false,
2091     dropEl : false, /// the dom placeholde element that indicates drop location.
2092     containerEl: false, // body container
2093     bodyEl: false, // card-body
2094     headerContainerEl : false, //
2095     headerEl : false,
2096     header_imageEl : false,
2097     
2098     layoutCls : function()
2099     {
2100         var cls = '';
2101         var t = this;
2102         Roo.log(this.margin_bottom.length);
2103         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2104             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2105             
2106             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2107                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2108             }
2109             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2110                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2111             }
2112         });
2113         
2114         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2115             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2116                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2117             }
2118         });
2119         
2120         // more generic support?
2121         if (this.hidden) {
2122             cls += ' d-none';
2123         }
2124         
2125         return cls;
2126     },
2127  
2128        // Roo.log("Call onRender: " + this.xtype);
2129         /*  We are looking at something like this.
2130 <div class="card">
2131     <img src="..." class="card-img-top" alt="...">
2132     <div class="card-body">
2133         <h5 class="card-title">Card title</h5>
2134          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2135
2136         >> this bit is really the body...
2137         <div> << we will ad dthis in hopefully it will not break shit.
2138         
2139         ** card text does not actually have any styling...
2140         
2141             <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>
2142         
2143         </div> <<
2144           <a href="#" class="card-link">Card link</a>
2145           
2146     </div>
2147     <div class="card-footer">
2148         <small class="text-muted">Last updated 3 mins ago</small>
2149     </div>
2150 </div>
2151          */
2152     getAutoCreate : function(){
2153         
2154         var cfg = {
2155             tag : 'div',
2156             cls : 'card',
2157             cn : [ ]
2158         };
2159         
2160         if (this.weight.length && this.weight != 'light') {
2161             cfg.cls += ' text-white';
2162         } else {
2163             cfg.cls += ' text-dark'; // need as it's nested..
2164         }
2165         if (this.weight.length) {
2166             cfg.cls += ' bg-' + this.weight;
2167         }
2168         
2169         cfg.cls += ' ' + this.layoutCls(); 
2170         
2171         var hdr = false;
2172         var hdr_ctr = false;
2173         if (this.header.length) {
2174             hdr = {
2175                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2176                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2177                 cn : []
2178             };
2179             cfg.cn.push(hdr);
2180             hdr_ctr = hdr;
2181         } else {
2182             hdr = {
2183                 tag : 'div',
2184                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2185                 cn : []
2186             };
2187             cfg.cn.push(hdr);
2188             hdr_ctr = hdr;
2189         }
2190         if (this.collapsable) {
2191             hdr_ctr = {
2192             tag : 'a',
2193             cls : 'd-block user-select-none',
2194             cn: [
2195                     {
2196                         tag: 'i',
2197                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2198                     }
2199                    
2200                 ]
2201             };
2202             hdr.cn.push(hdr_ctr);
2203         }
2204         
2205         hdr_ctr.cn.push(        {
2206             tag: 'span',
2207             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2208             html : this.header
2209         });
2210         
2211         
2212         if (this.header_image.length) {
2213             cfg.cn.push({
2214                 tag : 'img',
2215                 cls : 'card-img-top',
2216                 src: this.header_image // escape?
2217             });
2218         } else {
2219             cfg.cn.push({
2220                     tag : 'div',
2221                     cls : 'card-img-top d-none' 
2222                 });
2223         }
2224             
2225         var body = {
2226             tag : 'div',
2227             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2228             cn : []
2229         };
2230         var obody = body;
2231         if (this.collapsable || this.rotateable) {
2232             obody = {
2233                 tag: 'div',
2234                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2235                 cn : [  body ]
2236             };
2237         }
2238         
2239         cfg.cn.push(obody);
2240         
2241         if (this.title.length) {
2242             body.cn.push({
2243                 tag : 'div',
2244                 cls : 'card-title',
2245                 src: this.title // escape?
2246             });
2247         }  
2248         
2249         if (this.subtitle.length) {
2250             body.cn.push({
2251                 tag : 'div',
2252                 cls : 'card-title',
2253                 src: this.subtitle // escape?
2254             });
2255         }
2256         
2257         body.cn.push({
2258             tag : 'div',
2259             cls : 'roo-card-body-ctr'
2260         });
2261         
2262         if (this.html.length) {
2263             body.cn.push({
2264                 tag: 'div',
2265                 html : this.html
2266             });
2267         }
2268         // fixme ? handle objects?
2269         
2270         if (this.footer.length) {
2271            
2272             cfg.cn.push({
2273                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2274                 html : this.footer
2275             });
2276             
2277         } else {
2278             cfg.cn.push({cls : 'card-footer d-none'});
2279         }
2280         
2281         // footer...
2282         
2283         return cfg;
2284     },
2285     
2286     
2287     getCardHeader : function()
2288     {
2289         var  ret = this.el.select('.card-header',true).first();
2290         if (ret.hasClass('d-none')) {
2291             ret.removeClass('d-none');
2292         }
2293         
2294         return ret;
2295     },
2296     getCardFooter : function()
2297     {
2298         var  ret = this.el.select('.card-footer',true).first();
2299         if (ret.hasClass('d-none')) {
2300             ret.removeClass('d-none');
2301         }
2302         
2303         return ret;
2304     },
2305     getCardImageTop : function()
2306     {
2307         var  ret = this.header_imageEl;
2308         if (ret.hasClass('d-none')) {
2309             ret.removeClass('d-none');
2310         }
2311             
2312         return ret;
2313     },
2314     
2315     getChildContainer : function()
2316     {
2317         
2318         if(!this.el){
2319             return false;
2320         }
2321         return this.el.select('.roo-card-body-ctr',true).first();    
2322     },
2323     
2324     initEvents: function() 
2325     {
2326         this.bodyEl = this.el.select('.card-body',true).first(); 
2327         this.containerEl = this.getChildContainer();
2328         if(this.dragable){
2329             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2330                     containerScroll: true,
2331                     ddGroup: this.drag_group || 'default_card_drag_group'
2332             });
2333             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2334         }
2335         if (this.dropable) {
2336             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2337                 containerScroll: true,
2338                 ddGroup: this.drop_group || 'default_card_drag_group'
2339             });
2340             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2341             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2342             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2343             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2344             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2345         }
2346         
2347         if (this.collapsable) {
2348             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2349         }
2350         if (this.rotateable) {
2351             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2352         }
2353         this.collapsableEl = this.el.select('.roo-collapsable').first();
2354          
2355         this.footerEl = this.el.select('.card-footer').first();
2356         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2357         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2358         this.headerEl = this.el.select('.card-header',true).first();
2359         
2360         if (this.rotated) {
2361             this.el.addClass('roo-card-rotated');
2362             this.fireEvent('rotate', this, true);
2363         }
2364         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2365         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2366         
2367     },
2368     getDragData : function(e)
2369     {
2370         var target = this.getEl();
2371         if (target) {
2372             //this.handleSelection(e);
2373             
2374             var dragData = {
2375                 source: this,
2376                 copy: false,
2377                 nodes: this.getEl(),
2378                 records: []
2379             };
2380             
2381             
2382             dragData.ddel = target.dom ;    // the div element
2383             Roo.log(target.getWidth( ));
2384             dragData.ddel.style.width = target.getWidth() + 'px';
2385             
2386             return dragData;
2387         }
2388         return false;
2389     },
2390     /**
2391     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2392     *    whole Element becomes the target, and this causes the drop gesture to append.
2393     *
2394     *    Returns an object:
2395     *     {
2396            
2397            position : 'below' or 'above'
2398            card  : relateive to card OBJECT (or true for no cards listed)
2399            items_n : relative to nth item in list
2400            card_n : relative to  nth card in list
2401     }
2402     *
2403     *    
2404     */
2405     getTargetFromEvent : function(e, dragged_card_el)
2406     {
2407         var target = e.getTarget();
2408         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2409             target = target.parentNode;
2410         }
2411         
2412         var ret = {
2413             position: '',
2414             cards : [],
2415             card_n : -1,
2416             items_n : -1,
2417             card : false 
2418         };
2419         
2420         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2421         // see if target is one of the 'cards'...
2422         
2423         
2424         //Roo.log(this.items.length);
2425         var pos = false;
2426         
2427         var last_card_n = 0;
2428         var cards_len  = 0;
2429         for (var i = 0;i< this.items.length;i++) {
2430             
2431             if (!this.items[i].el.hasClass('card')) {
2432                  continue;
2433             }
2434             pos = this.getDropPoint(e, this.items[i].el.dom);
2435             
2436             cards_len = ret.cards.length;
2437             //Roo.log(this.items[i].el.dom.id);
2438             ret.cards.push(this.items[i]);
2439             last_card_n  = i;
2440             if (ret.card_n < 0 && pos == 'above') {
2441                 ret.position = cards_len > 0 ? 'below' : pos;
2442                 ret.items_n = i > 0 ? i - 1 : 0;
2443                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2444                 ret.card = ret.cards[ret.card_n];
2445             }
2446         }
2447         if (!ret.cards.length) {
2448             ret.card = true;
2449             ret.position = 'below';
2450             ret.items_n;
2451             return ret;
2452         }
2453         // could not find a card.. stick it at the end..
2454         if (ret.card_n < 0) {
2455             ret.card_n = last_card_n;
2456             ret.card = ret.cards[last_card_n];
2457             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2458             ret.position = 'below';
2459         }
2460         
2461         if (this.items[ret.items_n].el == dragged_card_el) {
2462             return false;
2463         }
2464         
2465         if (ret.position == 'below') {
2466             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2467             
2468             if (card_after  && card_after.el == dragged_card_el) {
2469                 return false;
2470             }
2471             return ret;
2472         }
2473         
2474         // its's after ..
2475         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2476         
2477         if (card_before  && card_before.el == dragged_card_el) {
2478             return false;
2479         }
2480         
2481         return ret;
2482     },
2483     
2484     onNodeEnter : function(n, dd, e, data){
2485         return false;
2486     },
2487     onNodeOver : function(n, dd, e, data)
2488     {
2489        
2490         var target_info = this.getTargetFromEvent(e,data.source.el);
2491         if (target_info === false) {
2492             this.dropPlaceHolder('hide');
2493             return false;
2494         }
2495         Roo.log(['getTargetFromEvent', target_info ]);
2496         
2497         
2498         if (this.fireEvent('cardover', this, [ data ]) === false) {
2499             return false;
2500         }
2501         
2502         this.dropPlaceHolder('show', target_info,data);
2503         
2504         return false; 
2505     },
2506     onNodeOut : function(n, dd, e, data){
2507         this.dropPlaceHolder('hide');
2508      
2509     },
2510     onNodeDrop : function(n, dd, e, data)
2511     {
2512         
2513         // call drop - return false if
2514         
2515         // this could actually fail - if the Network drops..
2516         // we will ignore this at present..- client should probably reload
2517         // the whole set of cards if stuff like that fails.
2518         
2519         
2520         var info = this.getTargetFromEvent(e,data.source.el);
2521         if (info === false) {
2522             return false;
2523         }
2524         this.dropPlaceHolder('hide');
2525   
2526           
2527     
2528         this.acceptCard(data.source, info.position, info.card, info.items_n);
2529         return true;
2530          
2531     },
2532     firstChildCard : function()
2533     {
2534         for (var i = 0;i< this.items.length;i++) {
2535             
2536             if (!this.items[i].el.hasClass('card')) {
2537                  continue;
2538             }
2539             return this.items[i];
2540         }
2541         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2542     },
2543     /**
2544      * accept card
2545      *
2546      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2547      */
2548     acceptCard : function(move_card,  position, next_to_card )
2549     {
2550         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2551             return false;
2552         }
2553         
2554         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2555         
2556         move_card.parent().removeCard(move_card);
2557         
2558         
2559         var dom = move_card.el.dom;
2560         dom.style.width = ''; // clear with - which is set by drag.
2561         
2562         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2563             var cardel = next_to_card.el.dom;
2564             
2565             if (position == 'above' ) {
2566                 cardel.parentNode.insertBefore(dom, cardel);
2567             } else if (cardel.nextSibling) {
2568                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2569             } else {
2570                 cardel.parentNode.append(dom);
2571             }
2572         } else {
2573             // card container???
2574             this.containerEl.dom.append(dom);
2575         }
2576         
2577         //FIXME HANDLE card = true 
2578         
2579         // add this to the correct place in items.
2580         
2581         // remove Card from items.
2582         
2583        
2584         if (this.items.length) {
2585             var nitems = [];
2586             //Roo.log([info.items_n, info.position, this.items.length]);
2587             for (var i =0; i < this.items.length; i++) {
2588                 if (i == to_items_n && position == 'above') {
2589                     nitems.push(move_card);
2590                 }
2591                 nitems.push(this.items[i]);
2592                 if (i == to_items_n && position == 'below') {
2593                     nitems.push(move_card);
2594                 }
2595             }
2596             this.items = nitems;
2597             Roo.log(this.items);
2598         } else {
2599             this.items.push(move_card);
2600         }
2601         
2602         move_card.parentId = this.id;
2603         
2604         return true;
2605         
2606         
2607     },
2608     removeCard : function(c)
2609     {
2610         this.items = this.items.filter(function(e) { return e != c });
2611  
2612         var dom = c.el.dom;
2613         dom.parentNode.removeChild(dom);
2614         dom.style.width = ''; // clear with - which is set by drag.
2615         c.parentId = false;
2616         
2617     },
2618     
2619     /**    Decide whether to drop above or below a View node. */
2620     getDropPoint : function(e, n, dd)
2621     {
2622         if (dd) {
2623              return false;
2624         }
2625         if (n == this.containerEl.dom) {
2626             return "above";
2627         }
2628         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2629         var c = t + (b - t) / 2;
2630         var y = Roo.lib.Event.getPageY(e);
2631         if(y <= c) {
2632             return "above";
2633         }else{
2634             return "below";
2635         }
2636     },
2637     onToggleCollapse : function(e)
2638         {
2639         if (this.collapsed) {
2640             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2641             this.collapsableEl.addClass('show');
2642             this.collapsed = false;
2643             return;
2644         }
2645         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2646         this.collapsableEl.removeClass('show');
2647         this.collapsed = true;
2648         
2649     
2650     },
2651     
2652     onToggleRotate : function(e)
2653     {
2654         this.collapsableEl.removeClass('show');
2655         this.footerEl.removeClass('d-none');
2656         this.el.removeClass('roo-card-rotated');
2657         this.el.removeClass('d-none');
2658         if (this.rotated) {
2659             
2660             this.collapsableEl.addClass('show');
2661             this.rotated = false;
2662             this.fireEvent('rotate', this, this.rotated);
2663             return;
2664         }
2665         this.el.addClass('roo-card-rotated');
2666         this.footerEl.addClass('d-none');
2667         this.el.select('.roo-collapsable').removeClass('show');
2668         
2669         this.rotated = true;
2670         this.fireEvent('rotate', this, this.rotated);
2671     
2672     },
2673     
2674     dropPlaceHolder: function (action, info, data)
2675     {
2676         if (this.dropEl === false) {
2677             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2678             cls : 'd-none'
2679             },true);
2680         }
2681         this.dropEl.removeClass(['d-none', 'd-block']);        
2682         if (action == 'hide') {
2683             
2684             this.dropEl.addClass('d-none');
2685             return;
2686         }
2687         // FIXME - info.card == true!!!
2688         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2689         
2690         if (info.card !== true) {
2691             var cardel = info.card.el.dom;
2692             
2693             if (info.position == 'above') {
2694                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2695             } else if (cardel.nextSibling) {
2696                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2697             } else {
2698                 cardel.parentNode.append(this.dropEl.dom);
2699             }
2700         } else {
2701             // card container???
2702             this.containerEl.dom.append(this.dropEl.dom);
2703         }
2704         
2705         this.dropEl.addClass('d-block roo-card-dropzone');
2706         
2707         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2708         
2709         
2710     
2711     
2712     
2713     },
2714     setHeaderText: function(html)
2715     {
2716         this.header = html;
2717         if (this.headerContainerEl) {
2718             this.headerContainerEl.dom.innerHTML = html;
2719         }
2720     },
2721     onHeaderImageLoad : function(ev, he)
2722     {
2723         if (!this.header_image_fit_square) {
2724             return;
2725         }
2726         
2727         var hw = he.naturalHeight / he.naturalWidth;
2728         // wide image = < 0
2729         // tall image = > 1
2730         //var w = he.dom.naturalWidth;
2731         var ww = he.width;
2732         he.style.left =  0;
2733         he.style.position =  'relative';
2734         if (hw > 1) {
2735             var nw = (ww * (1/hw));
2736             Roo.get(he).setSize( ww * (1/hw),  ww);
2737             he.style.left =  ((ww - nw)/ 2) + 'px';
2738             he.style.position =  'relative';
2739         }
2740
2741     }
2742
2743     
2744 });
2745
2746 /*
2747  * - LGPL
2748  *
2749  * Card header - holder for the card header elements.
2750  * 
2751  */
2752
2753 /**
2754  * @class Roo.bootstrap.CardHeader
2755  * @extends Roo.bootstrap.Element
2756  * Bootstrap CardHeader class
2757  * @constructor
2758  * Create a new Card Header - that you can embed children into
2759  * @param {Object} config The config object
2760  */
2761
2762 Roo.bootstrap.CardHeader = function(config){
2763     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2764 };
2765
2766 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2767     
2768     
2769     container_method : 'getCardHeader' 
2770     
2771      
2772     
2773     
2774    
2775 });
2776
2777  
2778
2779  /*
2780  * - LGPL
2781  *
2782  * Card footer - holder for the card footer elements.
2783  * 
2784  */
2785
2786 /**
2787  * @class Roo.bootstrap.CardFooter
2788  * @extends Roo.bootstrap.Element
2789  * Bootstrap CardFooter class
2790  * @constructor
2791  * Create a new Card Footer - that you can embed children into
2792  * @param {Object} config The config object
2793  */
2794
2795 Roo.bootstrap.CardFooter = function(config){
2796     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2797 };
2798
2799 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2800     
2801     
2802     container_method : 'getCardFooter' 
2803     
2804      
2805     
2806     
2807    
2808 });
2809
2810  
2811
2812  /*
2813  * - LGPL
2814  *
2815  * Card header - holder for the card header elements.
2816  * 
2817  */
2818
2819 /**
2820  * @class Roo.bootstrap.CardImageTop
2821  * @extends Roo.bootstrap.Element
2822  * Bootstrap CardImageTop class
2823  * @constructor
2824  * Create a new Card Image Top container
2825  * @param {Object} config The config object
2826  */
2827
2828 Roo.bootstrap.CardImageTop = function(config){
2829     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2830 };
2831
2832 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2833     
2834    
2835     container_method : 'getCardImageTop' 
2836     
2837      
2838     
2839    
2840 });
2841
2842  
2843
2844  /*
2845  * - LGPL
2846  *
2847  * image
2848  * 
2849  */
2850
2851
2852 /**
2853  * @class Roo.bootstrap.Img
2854  * @extends Roo.bootstrap.Component
2855  * Bootstrap Img class
2856  * @cfg {Boolean} imgResponsive false | true
2857  * @cfg {String} border rounded | circle | thumbnail
2858  * @cfg {String} src image source
2859  * @cfg {String} alt image alternative text
2860  * @cfg {String} href a tag href
2861  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2862  * @cfg {String} xsUrl xs image source
2863  * @cfg {String} smUrl sm image source
2864  * @cfg {String} mdUrl md image source
2865  * @cfg {String} lgUrl lg image source
2866  * 
2867  * @constructor
2868  * Create a new Input
2869  * @param {Object} config The config object
2870  */
2871
2872 Roo.bootstrap.Img = function(config){
2873     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2874     
2875     this.addEvents({
2876         // img events
2877         /**
2878          * @event click
2879          * The img click event for the img.
2880          * @param {Roo.EventObject} e
2881          */
2882         "click" : true
2883     });
2884 };
2885
2886 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2887     
2888     imgResponsive: true,
2889     border: '',
2890     src: 'about:blank',
2891     href: false,
2892     target: false,
2893     xsUrl: '',
2894     smUrl: '',
2895     mdUrl: '',
2896     lgUrl: '',
2897
2898     getAutoCreate : function()
2899     {   
2900         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2901             return this.createSingleImg();
2902         }
2903         
2904         var cfg = {
2905             tag: 'div',
2906             cls: 'roo-image-responsive-group',
2907             cn: []
2908         };
2909         var _this = this;
2910         
2911         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2912             
2913             if(!_this[size + 'Url']){
2914                 return;
2915             }
2916             
2917             var img = {
2918                 tag: 'img',
2919                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2920                 html: _this.html || cfg.html,
2921                 src: _this[size + 'Url']
2922             };
2923             
2924             img.cls += ' roo-image-responsive-' + size;
2925             
2926             var s = ['xs', 'sm', 'md', 'lg'];
2927             
2928             s.splice(s.indexOf(size), 1);
2929             
2930             Roo.each(s, function(ss){
2931                 img.cls += ' hidden-' + ss;
2932             });
2933             
2934             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2935                 cfg.cls += ' img-' + _this.border;
2936             }
2937             
2938             if(_this.alt){
2939                 cfg.alt = _this.alt;
2940             }
2941             
2942             if(_this.href){
2943                 var a = {
2944                     tag: 'a',
2945                     href: _this.href,
2946                     cn: [
2947                         img
2948                     ]
2949                 };
2950
2951                 if(this.target){
2952                     a.target = _this.target;
2953                 }
2954             }
2955             
2956             cfg.cn.push((_this.href) ? a : img);
2957             
2958         });
2959         
2960         return cfg;
2961     },
2962     
2963     createSingleImg : function()
2964     {
2965         var cfg = {
2966             tag: 'img',
2967             cls: (this.imgResponsive) ? 'img-responsive' : '',
2968             html : null,
2969             src : 'about:blank'  // just incase src get's set to undefined?!?
2970         };
2971         
2972         cfg.html = this.html || cfg.html;
2973         
2974         cfg.src = this.src || cfg.src;
2975         
2976         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2977             cfg.cls += ' img-' + this.border;
2978         }
2979         
2980         if(this.alt){
2981             cfg.alt = this.alt;
2982         }
2983         
2984         if(this.href){
2985             var a = {
2986                 tag: 'a',
2987                 href: this.href,
2988                 cn: [
2989                     cfg
2990                 ]
2991             };
2992             
2993             if(this.target){
2994                 a.target = this.target;
2995             }
2996             
2997         }
2998         
2999         return (this.href) ? a : cfg;
3000     },
3001     
3002     initEvents: function() 
3003     {
3004         if(!this.href){
3005             this.el.on('click', this.onClick, this);
3006         }
3007         
3008     },
3009     
3010     onClick : function(e)
3011     {
3012         Roo.log('img onclick');
3013         this.fireEvent('click', this, e);
3014     },
3015     /**
3016      * Sets the url of the image - used to update it
3017      * @param {String} url the url of the image
3018      */
3019     
3020     setSrc : function(url)
3021     {
3022         this.src =  url;
3023         
3024         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3025             this.el.dom.src =  url;
3026             return;
3027         }
3028         
3029         this.el.select('img', true).first().dom.src =  url;
3030     }
3031     
3032     
3033    
3034 });
3035
3036  /*
3037  * - LGPL
3038  *
3039  * image
3040  * 
3041  */
3042
3043
3044 /**
3045  * @class Roo.bootstrap.Link
3046  * @extends Roo.bootstrap.Component
3047  * Bootstrap Link Class
3048  * @cfg {String} alt image alternative text
3049  * @cfg {String} href a tag href
3050  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3051  * @cfg {String} html the content of the link.
3052  * @cfg {String} anchor name for the anchor link
3053  * @cfg {String} fa - favicon
3054
3055  * @cfg {Boolean} preventDefault (true | false) default false
3056
3057  * 
3058  * @constructor
3059  * Create a new Input
3060  * @param {Object} config The config object
3061  */
3062
3063 Roo.bootstrap.Link = function(config){
3064     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3065     
3066     this.addEvents({
3067         // img events
3068         /**
3069          * @event click
3070          * The img click event for the img.
3071          * @param {Roo.EventObject} e
3072          */
3073         "click" : true
3074     });
3075 };
3076
3077 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3078     
3079     href: false,
3080     target: false,
3081     preventDefault: false,
3082     anchor : false,
3083     alt : false,
3084     fa: false,
3085
3086
3087     getAutoCreate : function()
3088     {
3089         var html = this.html || '';
3090         
3091         if (this.fa !== false) {
3092             html = '<i class="fa fa-' + this.fa + '"></i>';
3093         }
3094         var cfg = {
3095             tag: 'a'
3096         };
3097         // anchor's do not require html/href...
3098         if (this.anchor === false) {
3099             cfg.html = html;
3100             cfg.href = this.href || '#';
3101         } else {
3102             cfg.name = this.anchor;
3103             if (this.html !== false || this.fa !== false) {
3104                 cfg.html = html;
3105             }
3106             if (this.href !== false) {
3107                 cfg.href = this.href;
3108             }
3109         }
3110         
3111         if(this.alt !== false){
3112             cfg.alt = this.alt;
3113         }
3114         
3115         
3116         if(this.target !== false) {
3117             cfg.target = this.target;
3118         }
3119         
3120         return cfg;
3121     },
3122     
3123     initEvents: function() {
3124         
3125         if(!this.href || this.preventDefault){
3126             this.el.on('click', this.onClick, this);
3127         }
3128     },
3129     
3130     onClick : function(e)
3131     {
3132         if(this.preventDefault){
3133             e.preventDefault();
3134         }
3135         //Roo.log('img onclick');
3136         this.fireEvent('click', this, e);
3137     }
3138    
3139 });
3140
3141  /*
3142  * - LGPL
3143  *
3144  * header
3145  * 
3146  */
3147
3148 /**
3149  * @class Roo.bootstrap.Header
3150  * @extends Roo.bootstrap.Component
3151  * Bootstrap Header class
3152  * @cfg {String} html content of header
3153  * @cfg {Number} level (1|2|3|4|5|6) default 1
3154  * 
3155  * @constructor
3156  * Create a new Header
3157  * @param {Object} config The config object
3158  */
3159
3160
3161 Roo.bootstrap.Header  = function(config){
3162     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3163 };
3164
3165 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3166     
3167     //href : false,
3168     html : false,
3169     level : 1,
3170     
3171     
3172     
3173     getAutoCreate : function(){
3174         
3175         
3176         
3177         var cfg = {
3178             tag: 'h' + (1 *this.level),
3179             html: this.html || ''
3180         } ;
3181         
3182         return cfg;
3183     }
3184    
3185 });
3186
3187  
3188
3189  /*
3190  * Based on:
3191  * Ext JS Library 1.1.1
3192  * Copyright(c) 2006-2007, Ext JS, LLC.
3193  *
3194  * Originally Released Under LGPL - original licence link has changed is not relivant.
3195  *
3196  * Fork - LGPL
3197  * <script type="text/javascript">
3198  */
3199  
3200 /**
3201  * @class Roo.bootstrap.MenuMgr
3202  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3203  * @singleton
3204  */
3205 Roo.bootstrap.MenuMgr = function(){
3206    var menus, active, groups = {}, attached = false, lastShow = new Date();
3207
3208    // private - called when first menu is created
3209    function init(){
3210        menus = {};
3211        active = new Roo.util.MixedCollection();
3212        Roo.get(document).addKeyListener(27, function(){
3213            if(active.length > 0){
3214                hideAll();
3215            }
3216        });
3217    }
3218
3219    // private
3220    function hideAll(){
3221        if(active && active.length > 0){
3222            var c = active.clone();
3223            c.each(function(m){
3224                m.hide();
3225            });
3226        }
3227    }
3228
3229    // private
3230    function onHide(m){
3231        active.remove(m);
3232        if(active.length < 1){
3233            Roo.get(document).un("mouseup", onMouseDown);
3234             
3235            attached = false;
3236        }
3237    }
3238
3239    // private
3240    function onShow(m){
3241        var last = active.last();
3242        lastShow = new Date();
3243        active.add(m);
3244        if(!attached){
3245           Roo.get(document).on("mouseup", onMouseDown);
3246            
3247            attached = true;
3248        }
3249        if(m.parentMenu){
3250           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3251           m.parentMenu.activeChild = m;
3252        }else if(last && last.isVisible()){
3253           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3254        }
3255    }
3256
3257    // private
3258    function onBeforeHide(m){
3259        if(m.activeChild){
3260            m.activeChild.hide();
3261        }
3262        if(m.autoHideTimer){
3263            clearTimeout(m.autoHideTimer);
3264            delete m.autoHideTimer;
3265        }
3266    }
3267
3268    // private
3269    function onBeforeShow(m){
3270        var pm = m.parentMenu;
3271        if(!pm && !m.allowOtherMenus){
3272            hideAll();
3273        }else if(pm && pm.activeChild && active != m){
3274            pm.activeChild.hide();
3275        }
3276    }
3277
3278    // private this should really trigger on mouseup..
3279    function onMouseDown(e){
3280         Roo.log("on Mouse Up");
3281         
3282         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3283             Roo.log("MenuManager hideAll");
3284             hideAll();
3285             e.stopEvent();
3286         }
3287         
3288         
3289    }
3290
3291    // private
3292    function onBeforeCheck(mi, state){
3293        if(state){
3294            var g = groups[mi.group];
3295            for(var i = 0, l = g.length; i < l; i++){
3296                if(g[i] != mi){
3297                    g[i].setChecked(false);
3298                }
3299            }
3300        }
3301    }
3302
3303    return {
3304
3305        /**
3306         * Hides all menus that are currently visible
3307         */
3308        hideAll : function(){
3309             hideAll();  
3310        },
3311
3312        // private
3313        register : function(menu){
3314            if(!menus){
3315                init();
3316            }
3317            menus[menu.id] = menu;
3318            menu.on("beforehide", onBeforeHide);
3319            menu.on("hide", onHide);
3320            menu.on("beforeshow", onBeforeShow);
3321            menu.on("show", onShow);
3322            var g = menu.group;
3323            if(g && menu.events["checkchange"]){
3324                if(!groups[g]){
3325                    groups[g] = [];
3326                }
3327                groups[g].push(menu);
3328                menu.on("checkchange", onCheck);
3329            }
3330        },
3331
3332         /**
3333          * Returns a {@link Roo.menu.Menu} object
3334          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3335          * be used to generate and return a new Menu instance.
3336          */
3337        get : function(menu){
3338            if(typeof menu == "string"){ // menu id
3339                return menus[menu];
3340            }else if(menu.events){  // menu instance
3341                return menu;
3342            }
3343            /*else if(typeof menu.length == 'number'){ // array of menu items?
3344                return new Roo.bootstrap.Menu({items:menu});
3345            }else{ // otherwise, must be a config
3346                return new Roo.bootstrap.Menu(menu);
3347            }
3348            */
3349            return false;
3350        },
3351
3352        // private
3353        unregister : function(menu){
3354            delete menus[menu.id];
3355            menu.un("beforehide", onBeforeHide);
3356            menu.un("hide", onHide);
3357            menu.un("beforeshow", onBeforeShow);
3358            menu.un("show", onShow);
3359            var g = menu.group;
3360            if(g && menu.events["checkchange"]){
3361                groups[g].remove(menu);
3362                menu.un("checkchange", onCheck);
3363            }
3364        },
3365
3366        // private
3367        registerCheckable : function(menuItem){
3368            var g = menuItem.group;
3369            if(g){
3370                if(!groups[g]){
3371                    groups[g] = [];
3372                }
3373                groups[g].push(menuItem);
3374                menuItem.on("beforecheckchange", onBeforeCheck);
3375            }
3376        },
3377
3378        // private
3379        unregisterCheckable : function(menuItem){
3380            var g = menuItem.group;
3381            if(g){
3382                groups[g].remove(menuItem);
3383                menuItem.un("beforecheckchange", onBeforeCheck);
3384            }
3385        }
3386    };
3387 }();/*
3388  * - LGPL
3389  *
3390  * menu
3391  * 
3392  */
3393
3394 /**
3395  * @class Roo.bootstrap.Menu
3396  * @extends Roo.bootstrap.Component
3397  * Bootstrap Menu class - container for MenuItems
3398  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3399  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3400  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3401  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3402  * 
3403  * @constructor
3404  * Create a new Menu
3405  * @param {Object} config The config object
3406  */
3407
3408
3409 Roo.bootstrap.Menu = function(config){
3410     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3411     if (this.registerMenu && this.type != 'treeview')  {
3412         Roo.bootstrap.MenuMgr.register(this);
3413     }
3414     
3415     
3416     this.addEvents({
3417         /**
3418          * @event beforeshow
3419          * Fires before this menu is displayed (return false to block)
3420          * @param {Roo.menu.Menu} this
3421          */
3422         beforeshow : true,
3423         /**
3424          * @event beforehide
3425          * Fires before this menu is hidden (return false to block)
3426          * @param {Roo.menu.Menu} this
3427          */
3428         beforehide : true,
3429         /**
3430          * @event show
3431          * Fires after this menu is displayed
3432          * @param {Roo.menu.Menu} this
3433          */
3434         show : true,
3435         /**
3436          * @event hide
3437          * Fires after this menu is hidden
3438          * @param {Roo.menu.Menu} this
3439          */
3440         hide : true,
3441         /**
3442          * @event click
3443          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3444          * @param {Roo.menu.Menu} this
3445          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3446          * @param {Roo.EventObject} e
3447          */
3448         click : true,
3449         /**
3450          * @event mouseover
3451          * Fires when the mouse is hovering over this menu
3452          * @param {Roo.menu.Menu} this
3453          * @param {Roo.EventObject} e
3454          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3455          */
3456         mouseover : true,
3457         /**
3458          * @event mouseout
3459          * Fires when the mouse exits this menu
3460          * @param {Roo.menu.Menu} this
3461          * @param {Roo.EventObject} e
3462          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3463          */
3464         mouseout : true,
3465         /**
3466          * @event itemclick
3467          * Fires when a menu item contained in this menu is clicked
3468          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3469          * @param {Roo.EventObject} e
3470          */
3471         itemclick: true
3472     });
3473     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3474 };
3475
3476 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3477     
3478    /// html : false,
3479     //align : '',
3480     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3481     type: false,
3482     /**
3483      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3484      */
3485     registerMenu : true,
3486     
3487     menuItems :false, // stores the menu items..
3488     
3489     hidden:true,
3490         
3491     parentMenu : false,
3492     
3493     stopEvent : true,
3494     
3495     isLink : false,
3496     
3497     getChildContainer : function() {
3498         return this.el;  
3499     },
3500     
3501     getAutoCreate : function(){
3502          
3503         //if (['right'].indexOf(this.align)!==-1) {
3504         //    cfg.cn[1].cls += ' pull-right'
3505         //}
3506         
3507         
3508         var cfg = {
3509             tag : 'ul',
3510             cls : 'dropdown-menu' ,
3511             style : 'z-index:1000'
3512             
3513         };
3514         
3515         if (this.type === 'submenu') {
3516             cfg.cls = 'submenu active';
3517         }
3518         if (this.type === 'treeview') {
3519             cfg.cls = 'treeview-menu';
3520         }
3521         
3522         return cfg;
3523     },
3524     initEvents : function() {
3525         
3526        // Roo.log("ADD event");
3527        // Roo.log(this.triggerEl.dom);
3528         
3529         this.triggerEl.on('click', this.onTriggerClick, this);
3530         
3531         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3532         
3533         
3534         if (this.triggerEl.hasClass('nav-item')) {
3535             // dropdown toggle on the 'a' in BS4?
3536             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3537         } else {
3538             this.triggerEl.addClass('dropdown-toggle');
3539         }
3540         if (Roo.isTouch) {
3541             this.el.on('touchstart'  , this.onTouch, this);
3542         }
3543         this.el.on('click' , this.onClick, this);
3544
3545         this.el.on("mouseover", this.onMouseOver, this);
3546         this.el.on("mouseout", this.onMouseOut, this);
3547         
3548     },
3549     
3550     findTargetItem : function(e)
3551     {
3552         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3553         if(!t){
3554             return false;
3555         }
3556         //Roo.log(t);         Roo.log(t.id);
3557         if(t && t.id){
3558             //Roo.log(this.menuitems);
3559             return this.menuitems.get(t.id);
3560             
3561             //return this.items.get(t.menuItemId);
3562         }
3563         
3564         return false;
3565     },
3566     
3567     onTouch : function(e) 
3568     {
3569         Roo.log("menu.onTouch");
3570         //e.stopEvent(); this make the user popdown broken
3571         this.onClick(e);
3572     },
3573     
3574     onClick : function(e)
3575     {
3576         Roo.log("menu.onClick");
3577         
3578         var t = this.findTargetItem(e);
3579         if(!t || t.isContainer){
3580             return;
3581         }
3582         Roo.log(e);
3583         /*
3584         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3585             if(t == this.activeItem && t.shouldDeactivate(e)){
3586                 this.activeItem.deactivate();
3587                 delete this.activeItem;
3588                 return;
3589             }
3590             if(t.canActivate){
3591                 this.setActiveItem(t, true);
3592             }
3593             return;
3594             
3595             
3596         }
3597         */
3598        
3599         Roo.log('pass click event');
3600         
3601         t.onClick(e);
3602         
3603         this.fireEvent("click", this, t, e);
3604         
3605         var _this = this;
3606         
3607         if(!t.href.length || t.href == '#'){
3608             (function() { _this.hide(); }).defer(100);
3609         }
3610         
3611     },
3612     
3613     onMouseOver : function(e){
3614         var t  = this.findTargetItem(e);
3615         //Roo.log(t);
3616         //if(t){
3617         //    if(t.canActivate && !t.disabled){
3618         //        this.setActiveItem(t, true);
3619         //    }
3620         //}
3621         
3622         this.fireEvent("mouseover", this, e, t);
3623     },
3624     isVisible : function(){
3625         return !this.hidden;
3626     },
3627     onMouseOut : function(e){
3628         var t  = this.findTargetItem(e);
3629         
3630         //if(t ){
3631         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3632         //        this.activeItem.deactivate();
3633         //        delete this.activeItem;
3634         //    }
3635         //}
3636         this.fireEvent("mouseout", this, e, t);
3637     },
3638     
3639     
3640     /**
3641      * Displays this menu relative to another element
3642      * @param {String/HTMLElement/Roo.Element} element The element to align to
3643      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3644      * the element (defaults to this.defaultAlign)
3645      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3646      */
3647     show : function(el, pos, parentMenu)
3648     {
3649         if (false === this.fireEvent("beforeshow", this)) {
3650             Roo.log("show canceled");
3651             return;
3652         }
3653         this.parentMenu = parentMenu;
3654         if(!this.el){
3655             this.render();
3656         }
3657         
3658         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3659     },
3660      /**
3661      * Displays this menu at a specific xy position
3662      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3663      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3664      */
3665     showAt : function(xy, parentMenu, /* private: */_e){
3666         this.parentMenu = parentMenu;
3667         if(!this.el){
3668             this.render();
3669         }
3670         if(_e !== false){
3671             this.fireEvent("beforeshow", this);
3672             //xy = this.el.adjustForConstraints(xy);
3673         }
3674         
3675         //this.el.show();
3676         this.hideMenuItems();
3677         this.hidden = false;
3678         this.triggerEl.addClass('open');
3679         this.el.addClass('show');
3680         
3681         // reassign x when hitting right
3682         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3683             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3684         }
3685         
3686         // reassign y when hitting bottom
3687         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3688             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3689         }
3690         
3691         // but the list may align on trigger left or trigger top... should it be a properity?
3692         
3693         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3694             this.el.setXY(xy);
3695         }
3696         
3697         this.focus();
3698         this.fireEvent("show", this);
3699     },
3700     
3701     focus : function(){
3702         return;
3703         if(!this.hidden){
3704             this.doFocus.defer(50, this);
3705         }
3706     },
3707
3708     doFocus : function(){
3709         if(!this.hidden){
3710             this.focusEl.focus();
3711         }
3712     },
3713
3714     /**
3715      * Hides this menu and optionally all parent menus
3716      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3717      */
3718     hide : function(deep)
3719     {
3720         if (false === this.fireEvent("beforehide", this)) {
3721             Roo.log("hide canceled");
3722             return;
3723         }
3724         this.hideMenuItems();
3725         if(this.el && this.isVisible()){
3726            
3727             if(this.activeItem){
3728                 this.activeItem.deactivate();
3729                 this.activeItem = null;
3730             }
3731             this.triggerEl.removeClass('open');;
3732             this.el.removeClass('show');
3733             this.hidden = true;
3734             this.fireEvent("hide", this);
3735         }
3736         if(deep === true && this.parentMenu){
3737             this.parentMenu.hide(true);
3738         }
3739     },
3740     
3741     onTriggerClick : function(e)
3742     {
3743         Roo.log('trigger click');
3744         
3745         var target = e.getTarget();
3746         
3747         Roo.log(target.nodeName.toLowerCase());
3748         
3749         if(target.nodeName.toLowerCase() === 'i'){
3750             e.preventDefault();
3751         }
3752         
3753     },
3754     
3755     onTriggerPress  : function(e)
3756     {
3757         Roo.log('trigger press');
3758         //Roo.log(e.getTarget());
3759        // Roo.log(this.triggerEl.dom);
3760        
3761         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3762         var pel = Roo.get(e.getTarget());
3763         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3764             Roo.log('is treeview or dropdown?');
3765             return;
3766         }
3767         
3768         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3769             return;
3770         }
3771         
3772         if (this.isVisible()) {
3773             Roo.log('hide');
3774             this.hide();
3775         } else {
3776             Roo.log('show');
3777             this.show(this.triggerEl, '?', false);
3778         }
3779         
3780         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3781             e.stopEvent();
3782         }
3783         
3784     },
3785        
3786     
3787     hideMenuItems : function()
3788     {
3789         Roo.log("hide Menu Items");
3790         if (!this.el) { 
3791             return;
3792         }
3793         
3794         this.el.select('.open',true).each(function(aa) {
3795             
3796             aa.removeClass('open');
3797          
3798         });
3799     },
3800     addxtypeChild : function (tree, cntr) {
3801         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3802           
3803         this.menuitems.add(comp);
3804         return comp;
3805
3806     },
3807     getEl : function()
3808     {
3809         Roo.log(this.el);
3810         return this.el;
3811     },
3812     
3813     clear : function()
3814     {
3815         this.getEl().dom.innerHTML = '';
3816         this.menuitems.clear();
3817     }
3818 });
3819
3820  
3821  /*
3822  * - LGPL
3823  *
3824  * menu item
3825  * 
3826  */
3827
3828
3829 /**
3830  * @class Roo.bootstrap.MenuItem
3831  * @extends Roo.bootstrap.Component
3832  * Bootstrap MenuItem class
3833  * @cfg {String} html the menu label
3834  * @cfg {String} href the link
3835  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3836  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3837  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3838  * @cfg {String} fa favicon to show on left of menu item.
3839  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3840  * 
3841  * 
3842  * @constructor
3843  * Create a new MenuItem
3844  * @param {Object} config The config object
3845  */
3846
3847
3848 Roo.bootstrap.MenuItem = function(config){
3849     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3850     this.addEvents({
3851         // raw events
3852         /**
3853          * @event click
3854          * The raw click event for the entire grid.
3855          * @param {Roo.bootstrap.MenuItem} this
3856          * @param {Roo.EventObject} e
3857          */
3858         "click" : true
3859     });
3860 };
3861
3862 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3863     
3864     href : false,
3865     html : false,
3866     preventDefault: false,
3867     isContainer : false,
3868     active : false,
3869     fa: false,
3870     
3871     getAutoCreate : function(){
3872         
3873         if(this.isContainer){
3874             return {
3875                 tag: 'li',
3876                 cls: 'dropdown-menu-item '
3877             };
3878         }
3879         var ctag = {
3880             tag: 'span',
3881             html: 'Link'
3882         };
3883         
3884         var anc = {
3885             tag : 'a',
3886             cls : 'dropdown-item',
3887             href : '#',
3888             cn : [  ]
3889         };
3890         
3891         if (this.fa !== false) {
3892             anc.cn.push({
3893                 tag : 'i',
3894                 cls : 'fa fa-' + this.fa
3895             });
3896         }
3897         
3898         anc.cn.push(ctag);
3899         
3900         
3901         var cfg= {
3902             tag: 'li',
3903             cls: 'dropdown-menu-item',
3904             cn: [ anc ]
3905         };
3906         if (this.parent().type == 'treeview') {
3907             cfg.cls = 'treeview-menu';
3908         }
3909         if (this.active) {
3910             cfg.cls += ' active';
3911         }
3912         
3913         
3914         
3915         anc.href = this.href || cfg.cn[0].href ;
3916         ctag.html = this.html || cfg.cn[0].html ;
3917         return cfg;
3918     },
3919     
3920     initEvents: function()
3921     {
3922         if (this.parent().type == 'treeview') {
3923             this.el.select('a').on('click', this.onClick, this);
3924         }
3925         
3926         if (this.menu) {
3927             this.menu.parentType = this.xtype;
3928             this.menu.triggerEl = this.el;
3929             this.menu = this.addxtype(Roo.apply({}, this.menu));
3930         }
3931         
3932     },
3933     onClick : function(e)
3934     {
3935         Roo.log('item on click ');
3936         
3937         if(this.preventDefault){
3938             e.preventDefault();
3939         }
3940         //this.parent().hideMenuItems();
3941         
3942         this.fireEvent('click', this, e);
3943     },
3944     getEl : function()
3945     {
3946         return this.el;
3947     } 
3948 });
3949
3950  
3951
3952  /*
3953  * - LGPL
3954  *
3955  * menu separator
3956  * 
3957  */
3958
3959
3960 /**
3961  * @class Roo.bootstrap.MenuSeparator
3962  * @extends Roo.bootstrap.Component
3963  * Bootstrap MenuSeparator class
3964  * 
3965  * @constructor
3966  * Create a new MenuItem
3967  * @param {Object} config The config object
3968  */
3969
3970
3971 Roo.bootstrap.MenuSeparator = function(config){
3972     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3973 };
3974
3975 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3976     
3977     getAutoCreate : function(){
3978         var cfg = {
3979             cls: 'divider',
3980             tag : 'li'
3981         };
3982         
3983         return cfg;
3984     }
3985    
3986 });
3987
3988  
3989
3990  
3991 /*
3992 * Licence: LGPL
3993 */
3994
3995 /**
3996  * @class Roo.bootstrap.Modal
3997  * @extends Roo.bootstrap.Component
3998  * Bootstrap Modal class
3999  * @cfg {String} title Title of dialog
4000  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4001  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4002  * @cfg {Boolean} specificTitle default false
4003  * @cfg {Array} buttons Array of buttons or standard button set..
4004  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4005  * @cfg {Boolean} animate default true
4006  * @cfg {Boolean} allow_close default true
4007  * @cfg {Boolean} fitwindow default false
4008  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4009  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4010  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4011  * @cfg {String} size (sm|lg|xl) default empty
4012  * @cfg {Number} max_width set the max width of modal
4013  * @cfg {Boolean} editableTitle can the title be edited
4014
4015  *
4016  *
4017  * @constructor
4018  * Create a new Modal Dialog
4019  * @param {Object} config The config object
4020  */
4021
4022 Roo.bootstrap.Modal = function(config){
4023     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4024     this.addEvents({
4025         // raw events
4026         /**
4027          * @event btnclick
4028          * The raw btnclick event for the button
4029          * @param {Roo.EventObject} e
4030          */
4031         "btnclick" : true,
4032         /**
4033          * @event resize
4034          * Fire when dialog resize
4035          * @param {Roo.bootstrap.Modal} this
4036          * @param {Roo.EventObject} e
4037          */
4038         "resize" : true,
4039         /**
4040          * @event titlechanged
4041          * Fire when the editable title has been changed
4042          * @param {Roo.bootstrap.Modal} this
4043          * @param {Roo.EventObject} value
4044          */
4045         "titlechanged" : true 
4046         
4047     });
4048     this.buttons = this.buttons || [];
4049
4050     if (this.tmpl) {
4051         this.tmpl = Roo.factory(this.tmpl);
4052     }
4053
4054 };
4055
4056 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4057
4058     title : 'test dialog',
4059
4060     buttons : false,
4061
4062     // set on load...
4063
4064     html: false,
4065
4066     tmp: false,
4067
4068     specificTitle: false,
4069
4070     buttonPosition: 'right',
4071
4072     allow_close : true,
4073
4074     animate : true,
4075
4076     fitwindow: false,
4077     
4078      // private
4079     dialogEl: false,
4080     bodyEl:  false,
4081     footerEl:  false,
4082     titleEl:  false,
4083     closeEl:  false,
4084
4085     size: '',
4086     
4087     max_width: 0,
4088     
4089     max_height: 0,
4090     
4091     fit_content: false,
4092     editableTitle  : false,
4093
4094     onRender : function(ct, position)
4095     {
4096         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4097
4098         if(!this.el){
4099             var cfg = Roo.apply({},  this.getAutoCreate());
4100             cfg.id = Roo.id();
4101             //if(!cfg.name){
4102             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4103             //}
4104             //if (!cfg.name.length) {
4105             //    delete cfg.name;
4106            // }
4107             if (this.cls) {
4108                 cfg.cls += ' ' + this.cls;
4109             }
4110             if (this.style) {
4111                 cfg.style = this.style;
4112             }
4113             this.el = Roo.get(document.body).createChild(cfg, position);
4114         }
4115         //var type = this.el.dom.type;
4116
4117
4118         if(this.tabIndex !== undefined){
4119             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4120         }
4121
4122         this.dialogEl = this.el.select('.modal-dialog',true).first();
4123         this.bodyEl = this.el.select('.modal-body',true).first();
4124         this.closeEl = this.el.select('.modal-header .close', true).first();
4125         this.headerEl = this.el.select('.modal-header',true).first();
4126         this.titleEl = this.el.select('.modal-title',true).first();
4127         this.footerEl = this.el.select('.modal-footer',true).first();
4128
4129         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4130         
4131         //this.el.addClass("x-dlg-modal");
4132
4133         if (this.buttons.length) {
4134             Roo.each(this.buttons, function(bb) {
4135                 var b = Roo.apply({}, bb);
4136                 b.xns = b.xns || Roo.bootstrap;
4137                 b.xtype = b.xtype || 'Button';
4138                 if (typeof(b.listeners) == 'undefined') {
4139                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4140                 }
4141
4142                 var btn = Roo.factory(b);
4143
4144                 btn.render(this.getButtonContainer());
4145
4146             },this);
4147         }
4148         // render the children.
4149         var nitems = [];
4150
4151         if(typeof(this.items) != 'undefined'){
4152             var items = this.items;
4153             delete this.items;
4154
4155             for(var i =0;i < items.length;i++) {
4156                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4157             }
4158         }
4159
4160         this.items = nitems;
4161
4162         // where are these used - they used to be body/close/footer
4163
4164
4165         this.initEvents();
4166         //this.el.addClass([this.fieldClass, this.cls]);
4167
4168     },
4169
4170     getAutoCreate : function()
4171     {
4172         // we will default to modal-body-overflow - might need to remove or make optional later.
4173         var bdy = {
4174                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4175                 html : this.html || ''
4176         };
4177
4178         var title = {
4179             tag: 'h5',
4180             cls : 'modal-title',
4181             html : this.title
4182         };
4183
4184         if(this.specificTitle){ // WTF is this?
4185             title = this.title;
4186         }
4187
4188         var header = [];
4189         if (this.allow_close && Roo.bootstrap.version == 3) {
4190             header.push({
4191                 tag: 'button',
4192                 cls : 'close',
4193                 html : '&times'
4194             });
4195         }
4196
4197         header.push(title);
4198
4199         if (this.editableTitle) {
4200             header.push({
4201                 cls: 'form-control roo-editable-title d-none',
4202                 tag: 'input',
4203                 type: 'text'
4204             });
4205         }
4206         
4207         if (this.allow_close && Roo.bootstrap.version == 4) {
4208             header.push({
4209                 tag: 'button',
4210                 cls : 'close',
4211                 html : '&times'
4212             });
4213         }
4214         
4215         var size = '';
4216
4217         if(this.size.length){
4218             size = 'modal-' + this.size;
4219         }
4220         
4221         var footer = Roo.bootstrap.version == 3 ?
4222             {
4223                 cls : 'modal-footer',
4224                 cn : [
4225                     {
4226                         tag: 'div',
4227                         cls: 'btn-' + this.buttonPosition
4228                     }
4229                 ]
4230
4231             } :
4232             {  // BS4 uses mr-auto on left buttons....
4233                 cls : 'modal-footer'
4234             };
4235
4236             
4237
4238         
4239         
4240         var modal = {
4241             cls: "modal",
4242              cn : [
4243                 {
4244                     cls: "modal-dialog " + size,
4245                     cn : [
4246                         {
4247                             cls : "modal-content",
4248                             cn : [
4249                                 {
4250                                     cls : 'modal-header',
4251                                     cn : header
4252                                 },
4253                                 bdy,
4254                                 footer
4255                             ]
4256
4257                         }
4258                     ]
4259
4260                 }
4261             ]
4262         };
4263
4264         if(this.animate){
4265             modal.cls += ' fade';
4266         }
4267
4268         return modal;
4269
4270     },
4271     getChildContainer : function() {
4272
4273          return this.bodyEl;
4274
4275     },
4276     getButtonContainer : function() {
4277         
4278          return Roo.bootstrap.version == 4 ?
4279             this.el.select('.modal-footer',true).first()
4280             : this.el.select('.modal-footer div',true).first();
4281
4282     },
4283     initEvents : function()
4284     {
4285         if (this.allow_close) {
4286             this.closeEl.on('click', this.hide, this);
4287         }
4288         Roo.EventManager.onWindowResize(this.resize, this, true);
4289         if (this.editableTitle) {
4290             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4291             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4292             this.headerEditEl.on('keyup', function(e) {
4293                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4294                         this.toggleHeaderInput(false)
4295                     }
4296                 }, this);
4297             this.headerEditEl.on('blur', function(e) {
4298                 this.toggleHeaderInput(false)
4299             },this);
4300         }
4301
4302     },
4303   
4304
4305     resize : function()
4306     {
4307         this.maskEl.setSize(
4308             Roo.lib.Dom.getViewWidth(true),
4309             Roo.lib.Dom.getViewHeight(true)
4310         );
4311         
4312         if (this.fitwindow) {
4313             
4314            this.dialogEl.setStyle( { 'max-width' : '100%' });
4315             this.setSize(
4316                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4317                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4318             );
4319             return;
4320         }
4321         
4322         if(this.max_width !== 0) {
4323             
4324             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4325             
4326             if(this.height) {
4327                 this.setSize(w, this.height);
4328                 return;
4329             }
4330             
4331             if(this.max_height) {
4332                 this.setSize(w,Math.min(
4333                     this.max_height,
4334                     Roo.lib.Dom.getViewportHeight(true) - 60
4335                 ));
4336                 
4337                 return;
4338             }
4339             
4340             if(!this.fit_content) {
4341                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4342                 return;
4343             }
4344             
4345             this.setSize(w, Math.min(
4346                 60 +
4347                 this.headerEl.getHeight() + 
4348                 this.footerEl.getHeight() + 
4349                 this.getChildHeight(this.bodyEl.dom.childNodes),
4350                 Roo.lib.Dom.getViewportHeight(true) - 60)
4351             );
4352         }
4353         
4354     },
4355
4356     setSize : function(w,h)
4357     {
4358         if (!w && !h) {
4359             return;
4360         }
4361         
4362         this.resizeTo(w,h);
4363     },
4364
4365     show : function() {
4366
4367         if (!this.rendered) {
4368             this.render();
4369         }
4370         this.toggleHeaderInput(false);
4371         //this.el.setStyle('display', 'block');
4372         this.el.removeClass('hideing');
4373         this.el.dom.style.display='block';
4374         
4375         Roo.get(document.body).addClass('modal-open');
4376  
4377         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4378             
4379             (function(){
4380                 this.el.addClass('show');
4381                 this.el.addClass('in');
4382             }).defer(50, this);
4383         }else{
4384             this.el.addClass('show');
4385             this.el.addClass('in');
4386         }
4387
4388         // not sure how we can show data in here..
4389         //if (this.tmpl) {
4390         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4391         //}
4392
4393         Roo.get(document.body).addClass("x-body-masked");
4394         
4395         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4396         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4397         this.maskEl.dom.style.display = 'block';
4398         this.maskEl.addClass('show');
4399         
4400         
4401         this.resize();
4402         
4403         this.fireEvent('show', this);
4404
4405         // set zindex here - otherwise it appears to be ignored...
4406         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4407
4408         (function () {
4409             this.items.forEach( function(e) {
4410                 e.layout ? e.layout() : false;
4411
4412             });
4413         }).defer(100,this);
4414
4415     },
4416     hide : function()
4417     {
4418         if(this.fireEvent("beforehide", this) !== false){
4419             
4420             this.maskEl.removeClass('show');
4421             
4422             this.maskEl.dom.style.display = '';
4423             Roo.get(document.body).removeClass("x-body-masked");
4424             this.el.removeClass('in');
4425             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4426
4427             if(this.animate){ // why
4428                 this.el.addClass('hideing');
4429                 this.el.removeClass('show');
4430                 (function(){
4431                     if (!this.el.hasClass('hideing')) {
4432                         return; // it's been shown again...
4433                     }
4434                     
4435                     this.el.dom.style.display='';
4436
4437                     Roo.get(document.body).removeClass('modal-open');
4438                     this.el.removeClass('hideing');
4439                 }).defer(150,this);
4440                 
4441             }else{
4442                 this.el.removeClass('show');
4443                 this.el.dom.style.display='';
4444                 Roo.get(document.body).removeClass('modal-open');
4445
4446             }
4447             this.fireEvent('hide', this);
4448         }
4449     },
4450     isVisible : function()
4451     {
4452         
4453         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4454         
4455     },
4456
4457     addButton : function(str, cb)
4458     {
4459
4460
4461         var b = Roo.apply({}, { html : str } );
4462         b.xns = b.xns || Roo.bootstrap;
4463         b.xtype = b.xtype || 'Button';
4464         if (typeof(b.listeners) == 'undefined') {
4465             b.listeners = { click : cb.createDelegate(this)  };
4466         }
4467
4468         var btn = Roo.factory(b);
4469
4470         btn.render(this.getButtonContainer());
4471
4472         return btn;
4473
4474     },
4475
4476     setDefaultButton : function(btn)
4477     {
4478         //this.el.select('.modal-footer').()
4479     },
4480
4481     resizeTo: function(w,h)
4482     {
4483         this.dialogEl.setWidth(w);
4484         
4485         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4486
4487         this.bodyEl.setHeight(h - diff);
4488         
4489         this.fireEvent('resize', this);
4490     },
4491     
4492     setContentSize  : function(w, h)
4493     {
4494
4495     },
4496     onButtonClick: function(btn,e)
4497     {
4498         //Roo.log([a,b,c]);
4499         this.fireEvent('btnclick', btn.name, e);
4500     },
4501      /**
4502      * Set the title of the Dialog
4503      * @param {String} str new Title
4504      */
4505     setTitle: function(str) {
4506         this.titleEl.dom.innerHTML = str;
4507         this.title = str;
4508     },
4509     /**
4510      * Set the body of the Dialog
4511      * @param {String} str new Title
4512      */
4513     setBody: function(str) {
4514         this.bodyEl.dom.innerHTML = str;
4515     },
4516     /**
4517      * Set the body of the Dialog using the template
4518      * @param {Obj} data - apply this data to the template and replace the body contents.
4519      */
4520     applyBody: function(obj)
4521     {
4522         if (!this.tmpl) {
4523             Roo.log("Error - using apply Body without a template");
4524             //code
4525         }
4526         this.tmpl.overwrite(this.bodyEl, obj);
4527     },
4528     
4529     getChildHeight : function(child_nodes)
4530     {
4531         if(
4532             !child_nodes ||
4533             child_nodes.length == 0
4534         ) {
4535             return 0;
4536         }
4537         
4538         var child_height = 0;
4539         
4540         for(var i = 0; i < child_nodes.length; i++) {
4541             
4542             /*
4543             * for modal with tabs...
4544             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4545                 
4546                 var layout_childs = child_nodes[i].childNodes;
4547                 
4548                 for(var j = 0; j < layout_childs.length; j++) {
4549                     
4550                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4551                         
4552                         var layout_body_childs = layout_childs[j].childNodes;
4553                         
4554                         for(var k = 0; k < layout_body_childs.length; k++) {
4555                             
4556                             if(layout_body_childs[k].classList.contains('navbar')) {
4557                                 child_height += layout_body_childs[k].offsetHeight;
4558                                 continue;
4559                             }
4560                             
4561                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4562                                 
4563                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4564                                 
4565                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4566                                     
4567                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4568                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4569                                         continue;
4570                                     }
4571                                     
4572                                 }
4573                                 
4574                             }
4575                             
4576                         }
4577                     }
4578                 }
4579                 continue;
4580             }
4581             */
4582             
4583             child_height += child_nodes[i].offsetHeight;
4584             // Roo.log(child_nodes[i].offsetHeight);
4585         }
4586         
4587         return child_height;
4588     },
4589     toggleHeaderInput : function(is_edit)
4590     {
4591         if (!this.editableTitle) {
4592             return; // not editable.
4593         }
4594         if (is_edit && this.is_header_editing) {
4595             return; // already editing..
4596         }
4597         if (is_edit) {
4598     
4599             this.headerEditEl.dom.value = this.title;
4600             this.headerEditEl.removeClass('d-none');
4601             this.headerEditEl.dom.focus();
4602             this.titleEl.addClass('d-none');
4603             
4604             this.is_header_editing = true;
4605             return
4606         }
4607         // flip back to not editing.
4608         this.title = this.headerEditEl.dom.value;
4609         this.headerEditEl.addClass('d-none');
4610         this.titleEl.removeClass('d-none');
4611         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4612         this.is_header_editing = false;
4613         this.fireEvent('titlechanged', this, this.title);
4614     
4615             
4616         
4617     }
4618
4619 });
4620
4621
4622 Roo.apply(Roo.bootstrap.Modal,  {
4623     /**
4624          * Button config that displays a single OK button
4625          * @type Object
4626          */
4627         OK :  [{
4628             name : 'ok',
4629             weight : 'primary',
4630             html : 'OK'
4631         }],
4632         /**
4633          * Button config that displays Yes and No buttons
4634          * @type Object
4635          */
4636         YESNO : [
4637             {
4638                 name  : 'no',
4639                 html : 'No'
4640             },
4641             {
4642                 name  :'yes',
4643                 weight : 'primary',
4644                 html : 'Yes'
4645             }
4646         ],
4647
4648         /**
4649          * Button config that displays OK and Cancel buttons
4650          * @type Object
4651          */
4652         OKCANCEL : [
4653             {
4654                name : 'cancel',
4655                 html : 'Cancel'
4656             },
4657             {
4658                 name : 'ok',
4659                 weight : 'primary',
4660                 html : 'OK'
4661             }
4662         ],
4663         /**
4664          * Button config that displays Yes, No and Cancel buttons
4665          * @type Object
4666          */
4667         YESNOCANCEL : [
4668             {
4669                 name : 'yes',
4670                 weight : 'primary',
4671                 html : 'Yes'
4672             },
4673             {
4674                 name : 'no',
4675                 html : 'No'
4676             },
4677             {
4678                 name : 'cancel',
4679                 html : 'Cancel'
4680             }
4681         ],
4682         
4683         zIndex : 10001
4684 });
4685
4686 /*
4687  * - LGPL
4688  *
4689  * messagebox - can be used as a replace
4690  * 
4691  */
4692 /**
4693  * @class Roo.MessageBox
4694  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4695  * Example usage:
4696  *<pre><code>
4697 // Basic alert:
4698 Roo.Msg.alert('Status', 'Changes saved successfully.');
4699
4700 // Prompt for user data:
4701 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4702     if (btn == 'ok'){
4703         // process text value...
4704     }
4705 });
4706
4707 // Show a dialog using config options:
4708 Roo.Msg.show({
4709    title:'Save Changes?',
4710    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4711    buttons: Roo.Msg.YESNOCANCEL,
4712    fn: processResult,
4713    animEl: 'elId'
4714 });
4715 </code></pre>
4716  * @singleton
4717  */
4718 Roo.bootstrap.MessageBox = function(){
4719     var dlg, opt, mask, waitTimer;
4720     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4721     var buttons, activeTextEl, bwidth;
4722
4723     
4724     // private
4725     var handleButton = function(button){
4726         dlg.hide();
4727         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4728     };
4729
4730     // private
4731     var handleHide = function(){
4732         if(opt && opt.cls){
4733             dlg.el.removeClass(opt.cls);
4734         }
4735         //if(waitTimer){
4736         //    Roo.TaskMgr.stop(waitTimer);
4737         //    waitTimer = null;
4738         //}
4739     };
4740
4741     // private
4742     var updateButtons = function(b){
4743         var width = 0;
4744         if(!b){
4745             buttons["ok"].hide();
4746             buttons["cancel"].hide();
4747             buttons["yes"].hide();
4748             buttons["no"].hide();
4749             dlg.footerEl.hide();
4750             
4751             return width;
4752         }
4753         dlg.footerEl.show();
4754         for(var k in buttons){
4755             if(typeof buttons[k] != "function"){
4756                 if(b[k]){
4757                     buttons[k].show();
4758                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4759                     width += buttons[k].el.getWidth()+15;
4760                 }else{
4761                     buttons[k].hide();
4762                 }
4763             }
4764         }
4765         return width;
4766     };
4767
4768     // private
4769     var handleEsc = function(d, k, e){
4770         if(opt && opt.closable !== false){
4771             dlg.hide();
4772         }
4773         if(e){
4774             e.stopEvent();
4775         }
4776     };
4777
4778     return {
4779         /**
4780          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4781          * @return {Roo.BasicDialog} The BasicDialog element
4782          */
4783         getDialog : function(){
4784            if(!dlg){
4785                 dlg = new Roo.bootstrap.Modal( {
4786                     //draggable: true,
4787                     //resizable:false,
4788                     //constraintoviewport:false,
4789                     //fixedcenter:true,
4790                     //collapsible : false,
4791                     //shim:true,
4792                     //modal: true,
4793                 //    width: 'auto',
4794                   //  height:100,
4795                     //buttonAlign:"center",
4796                     closeClick : function(){
4797                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4798                             handleButton("no");
4799                         }else{
4800                             handleButton("cancel");
4801                         }
4802                     }
4803                 });
4804                 dlg.render();
4805                 dlg.on("hide", handleHide);
4806                 mask = dlg.mask;
4807                 //dlg.addKeyListener(27, handleEsc);
4808                 buttons = {};
4809                 this.buttons = buttons;
4810                 var bt = this.buttonText;
4811                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4812                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4813                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4814                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4815                 //Roo.log(buttons);
4816                 bodyEl = dlg.bodyEl.createChild({
4817
4818                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4819                         '<textarea class="roo-mb-textarea"></textarea>' +
4820                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4821                 });
4822                 msgEl = bodyEl.dom.firstChild;
4823                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4824                 textboxEl.enableDisplayMode();
4825                 textboxEl.addKeyListener([10,13], function(){
4826                     if(dlg.isVisible() && opt && opt.buttons){
4827                         if(opt.buttons.ok){
4828                             handleButton("ok");
4829                         }else if(opt.buttons.yes){
4830                             handleButton("yes");
4831                         }
4832                     }
4833                 });
4834                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4835                 textareaEl.enableDisplayMode();
4836                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4837                 progressEl.enableDisplayMode();
4838                 
4839                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4840                 var pf = progressEl.dom.firstChild;
4841                 if (pf) {
4842                     pp = Roo.get(pf.firstChild);
4843                     pp.setHeight(pf.offsetHeight);
4844                 }
4845                 
4846             }
4847             return dlg;
4848         },
4849
4850         /**
4851          * Updates the message box body text
4852          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4853          * the XHTML-compliant non-breaking space character '&amp;#160;')
4854          * @return {Roo.MessageBox} This message box
4855          */
4856         updateText : function(text)
4857         {
4858             if(!dlg.isVisible() && !opt.width){
4859                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4860                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4861             }
4862             msgEl.innerHTML = text || '&#160;';
4863       
4864             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4865             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4866             var w = Math.max(
4867                     Math.min(opt.width || cw , this.maxWidth), 
4868                     Math.max(opt.minWidth || this.minWidth, bwidth)
4869             );
4870             if(opt.prompt){
4871                 activeTextEl.setWidth(w);
4872             }
4873             if(dlg.isVisible()){
4874                 dlg.fixedcenter = false;
4875             }
4876             // to big, make it scroll. = But as usual stupid IE does not support
4877             // !important..
4878             
4879             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4880                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4881                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4882             } else {
4883                 bodyEl.dom.style.height = '';
4884                 bodyEl.dom.style.overflowY = '';
4885             }
4886             if (cw > w) {
4887                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4888             } else {
4889                 bodyEl.dom.style.overflowX = '';
4890             }
4891             
4892             dlg.setContentSize(w, bodyEl.getHeight());
4893             if(dlg.isVisible()){
4894                 dlg.fixedcenter = true;
4895             }
4896             return this;
4897         },
4898
4899         /**
4900          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4901          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4902          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4903          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4904          * @return {Roo.MessageBox} This message box
4905          */
4906         updateProgress : function(value, text){
4907             if(text){
4908                 this.updateText(text);
4909             }
4910             
4911             if (pp) { // weird bug on my firefox - for some reason this is not defined
4912                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4913                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4914             }
4915             return this;
4916         },        
4917
4918         /**
4919          * Returns true if the message box is currently displayed
4920          * @return {Boolean} True if the message box is visible, else false
4921          */
4922         isVisible : function(){
4923             return dlg && dlg.isVisible();  
4924         },
4925
4926         /**
4927          * Hides the message box if it is displayed
4928          */
4929         hide : function(){
4930             if(this.isVisible()){
4931                 dlg.hide();
4932             }  
4933         },
4934
4935         /**
4936          * Displays a new message box, or reinitializes an existing message box, based on the config options
4937          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4938          * The following config object properties are supported:
4939          * <pre>
4940 Property    Type             Description
4941 ----------  ---------------  ------------------------------------------------------------------------------------
4942 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4943                                    closes (defaults to undefined)
4944 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4945                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4946 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4947                                    progress and wait dialogs will ignore this property and always hide the
4948                                    close button as they can only be closed programmatically.
4949 cls               String           A custom CSS class to apply to the message box element
4950 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4951                                    displayed (defaults to 75)
4952 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4953                                    function will be btn (the name of the button that was clicked, if applicable,
4954                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4955                                    Progress and wait dialogs will ignore this option since they do not respond to
4956                                    user actions and can only be closed programmatically, so any required function
4957                                    should be called by the same code after it closes the dialog.
4958 icon              String           A CSS class that provides a background image to be used as an icon for
4959                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4960 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4961 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4962 modal             Boolean          False to allow user interaction with the page while the message box is
4963                                    displayed (defaults to true)
4964 msg               String           A string that will replace the existing message box body text (defaults
4965                                    to the XHTML-compliant non-breaking space character '&#160;')
4966 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4967 progress          Boolean          True to display a progress bar (defaults to false)
4968 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4969 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4970 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4971 title             String           The title text
4972 value             String           The string value to set into the active textbox element if displayed
4973 wait              Boolean          True to display a progress bar (defaults to false)
4974 width             Number           The width of the dialog in pixels
4975 </pre>
4976          *
4977          * Example usage:
4978          * <pre><code>
4979 Roo.Msg.show({
4980    title: 'Address',
4981    msg: 'Please enter your address:',
4982    width: 300,
4983    buttons: Roo.MessageBox.OKCANCEL,
4984    multiline: true,
4985    fn: saveAddress,
4986    animEl: 'addAddressBtn'
4987 });
4988 </code></pre>
4989          * @param {Object} config Configuration options
4990          * @return {Roo.MessageBox} This message box
4991          */
4992         show : function(options)
4993         {
4994             
4995             // this causes nightmares if you show one dialog after another
4996             // especially on callbacks..
4997              
4998             if(this.isVisible()){
4999                 
5000                 this.hide();
5001                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5002                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5003                 Roo.log("New Dialog Message:" +  options.msg )
5004                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5005                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5006                 
5007             }
5008             var d = this.getDialog();
5009             opt = options;
5010             d.setTitle(opt.title || "&#160;");
5011             d.closeEl.setDisplayed(opt.closable !== false);
5012             activeTextEl = textboxEl;
5013             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5014             if(opt.prompt){
5015                 if(opt.multiline){
5016                     textboxEl.hide();
5017                     textareaEl.show();
5018                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5019                         opt.multiline : this.defaultTextHeight);
5020                     activeTextEl = textareaEl;
5021                 }else{
5022                     textboxEl.show();
5023                     textareaEl.hide();
5024                 }
5025             }else{
5026                 textboxEl.hide();
5027                 textareaEl.hide();
5028             }
5029             progressEl.setDisplayed(opt.progress === true);
5030             if (opt.progress) {
5031                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5032             }
5033             this.updateProgress(0);
5034             activeTextEl.dom.value = opt.value || "";
5035             if(opt.prompt){
5036                 dlg.setDefaultButton(activeTextEl);
5037             }else{
5038                 var bs = opt.buttons;
5039                 var db = null;
5040                 if(bs && bs.ok){
5041                     db = buttons["ok"];
5042                 }else if(bs && bs.yes){
5043                     db = buttons["yes"];
5044                 }
5045                 dlg.setDefaultButton(db);
5046             }
5047             bwidth = updateButtons(opt.buttons);
5048             this.updateText(opt.msg);
5049             if(opt.cls){
5050                 d.el.addClass(opt.cls);
5051             }
5052             d.proxyDrag = opt.proxyDrag === true;
5053             d.modal = opt.modal !== false;
5054             d.mask = opt.modal !== false ? mask : false;
5055             if(!d.isVisible()){
5056                 // force it to the end of the z-index stack so it gets a cursor in FF
5057                 document.body.appendChild(dlg.el.dom);
5058                 d.animateTarget = null;
5059                 d.show(options.animEl);
5060             }
5061             return this;
5062         },
5063
5064         /**
5065          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5066          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5067          * and closing the message box when the process is complete.
5068          * @param {String} title The title bar text
5069          * @param {String} msg The message box body text
5070          * @return {Roo.MessageBox} This message box
5071          */
5072         progress : function(title, msg){
5073             this.show({
5074                 title : title,
5075                 msg : msg,
5076                 buttons: false,
5077                 progress:true,
5078                 closable:false,
5079                 minWidth: this.minProgressWidth,
5080                 modal : true
5081             });
5082             return this;
5083         },
5084
5085         /**
5086          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5087          * If a callback function is passed it will be called after the user clicks the button, and the
5088          * id of the button that was clicked will be passed as the only parameter to the callback
5089          * (could also be the top-right close button).
5090          * @param {String} title The title bar text
5091          * @param {String} msg The message box body text
5092          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5093          * @param {Object} scope (optional) The scope of the callback function
5094          * @return {Roo.MessageBox} This message box
5095          */
5096         alert : function(title, msg, fn, scope)
5097         {
5098             this.show({
5099                 title : title,
5100                 msg : msg,
5101                 buttons: this.OK,
5102                 fn: fn,
5103                 closable : false,
5104                 scope : scope,
5105                 modal : true
5106             });
5107             return this;
5108         },
5109
5110         /**
5111          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5112          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5113          * You are responsible for closing the message box when the process is complete.
5114          * @param {String} msg The message box body text
5115          * @param {String} title (optional) The title bar text
5116          * @return {Roo.MessageBox} This message box
5117          */
5118         wait : function(msg, title){
5119             this.show({
5120                 title : title,
5121                 msg : msg,
5122                 buttons: false,
5123                 closable:false,
5124                 progress:true,
5125                 modal:true,
5126                 width:300,
5127                 wait:true
5128             });
5129             waitTimer = Roo.TaskMgr.start({
5130                 run: function(i){
5131                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5132                 },
5133                 interval: 1000
5134             });
5135             return this;
5136         },
5137
5138         /**
5139          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5140          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5141          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5142          * @param {String} title The title bar text
5143          * @param {String} msg The message box body text
5144          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5145          * @param {Object} scope (optional) The scope of the callback function
5146          * @return {Roo.MessageBox} This message box
5147          */
5148         confirm : function(title, msg, fn, scope){
5149             this.show({
5150                 title : title,
5151                 msg : msg,
5152                 buttons: this.YESNO,
5153                 fn: fn,
5154                 scope : scope,
5155                 modal : true
5156             });
5157             return this;
5158         },
5159
5160         /**
5161          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5162          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5163          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5164          * (could also be the top-right close button) and the text that was entered will be passed as the two
5165          * parameters to the callback.
5166          * @param {String} title The title bar text
5167          * @param {String} msg The message box body text
5168          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5169          * @param {Object} scope (optional) The scope of the callback function
5170          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5171          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5172          * @return {Roo.MessageBox} This message box
5173          */
5174         prompt : function(title, msg, fn, scope, multiline){
5175             this.show({
5176                 title : title,
5177                 msg : msg,
5178                 buttons: this.OKCANCEL,
5179                 fn: fn,
5180                 minWidth:250,
5181                 scope : scope,
5182                 prompt:true,
5183                 multiline: multiline,
5184                 modal : true
5185             });
5186             return this;
5187         },
5188
5189         /**
5190          * Button config that displays a single OK button
5191          * @type Object
5192          */
5193         OK : {ok:true},
5194         /**
5195          * Button config that displays Yes and No buttons
5196          * @type Object
5197          */
5198         YESNO : {yes:true, no:true},
5199         /**
5200          * Button config that displays OK and Cancel buttons
5201          * @type Object
5202          */
5203         OKCANCEL : {ok:true, cancel:true},
5204         /**
5205          * Button config that displays Yes, No and Cancel buttons
5206          * @type Object
5207          */
5208         YESNOCANCEL : {yes:true, no:true, cancel:true},
5209
5210         /**
5211          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5212          * @type Number
5213          */
5214         defaultTextHeight : 75,
5215         /**
5216          * The maximum width in pixels of the message box (defaults to 600)
5217          * @type Number
5218          */
5219         maxWidth : 600,
5220         /**
5221          * The minimum width in pixels of the message box (defaults to 100)
5222          * @type Number
5223          */
5224         minWidth : 100,
5225         /**
5226          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5227          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5228          * @type Number
5229          */
5230         minProgressWidth : 250,
5231         /**
5232          * An object containing the default button text strings that can be overriden for localized language support.
5233          * Supported properties are: ok, cancel, yes and no.
5234          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5235          * @type Object
5236          */
5237         buttonText : {
5238             ok : "OK",
5239             cancel : "Cancel",
5240             yes : "Yes",
5241             no : "No"
5242         }
5243     };
5244 }();
5245
5246 /**
5247  * Shorthand for {@link Roo.MessageBox}
5248  */
5249 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5250 Roo.Msg = Roo.Msg || Roo.MessageBox;
5251 /*
5252  * - LGPL
5253  *
5254  * navbar
5255  * 
5256  */
5257
5258 /**
5259  * @class Roo.bootstrap.Navbar
5260  * @extends Roo.bootstrap.Component
5261  * Bootstrap Navbar class
5262
5263  * @constructor
5264  * Create a new Navbar
5265  * @param {Object} config The config object
5266  */
5267
5268
5269 Roo.bootstrap.Navbar = function(config){
5270     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5271     this.addEvents({
5272         // raw events
5273         /**
5274          * @event beforetoggle
5275          * Fire before toggle the menu
5276          * @param {Roo.EventObject} e
5277          */
5278         "beforetoggle" : true
5279     });
5280 };
5281
5282 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5283     
5284     
5285    
5286     // private
5287     navItems : false,
5288     loadMask : false,
5289     
5290     
5291     getAutoCreate : function(){
5292         
5293         
5294         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5295         
5296     },
5297     
5298     initEvents :function ()
5299     {
5300         //Roo.log(this.el.select('.navbar-toggle',true));
5301         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5302         
5303         var mark = {
5304             tag: "div",
5305             cls:"x-dlg-mask"
5306         };
5307         
5308         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5309         
5310         var size = this.el.getSize();
5311         this.maskEl.setSize(size.width, size.height);
5312         this.maskEl.enableDisplayMode("block");
5313         this.maskEl.hide();
5314         
5315         if(this.loadMask){
5316             this.maskEl.show();
5317         }
5318     },
5319     
5320     
5321     getChildContainer : function()
5322     {
5323         if (this.el && this.el.select('.collapse').getCount()) {
5324             return this.el.select('.collapse',true).first();
5325         }
5326         
5327         return this.el;
5328     },
5329     
5330     mask : function()
5331     {
5332         this.maskEl.show();
5333     },
5334     
5335     unmask : function()
5336     {
5337         this.maskEl.hide();
5338     },
5339     onToggle : function()
5340     {
5341         
5342         if(this.fireEvent('beforetoggle', this) === false){
5343             return;
5344         }
5345         var ce = this.el.select('.navbar-collapse',true).first();
5346       
5347         if (!ce.hasClass('show')) {
5348            this.expand();
5349         } else {
5350             this.collapse();
5351         }
5352         
5353         
5354     
5355     },
5356     /**
5357      * Expand the navbar pulldown 
5358      */
5359     expand : function ()
5360     {
5361        
5362         var ce = this.el.select('.navbar-collapse',true).first();
5363         if (ce.hasClass('collapsing')) {
5364             return;
5365         }
5366         ce.dom.style.height = '';
5367                // show it...
5368         ce.addClass('in'); // old...
5369         ce.removeClass('collapse');
5370         ce.addClass('show');
5371         var h = ce.getHeight();
5372         Roo.log(h);
5373         ce.removeClass('show');
5374         // at this point we should be able to see it..
5375         ce.addClass('collapsing');
5376         
5377         ce.setHeight(0); // resize it ...
5378         ce.on('transitionend', function() {
5379             //Roo.log('done transition');
5380             ce.removeClass('collapsing');
5381             ce.addClass('show');
5382             ce.removeClass('collapse');
5383
5384             ce.dom.style.height = '';
5385         }, this, { single: true} );
5386         ce.setHeight(h);
5387         ce.dom.scrollTop = 0;
5388     },
5389     /**
5390      * Collapse the navbar pulldown 
5391      */
5392     collapse : function()
5393     {
5394          var ce = this.el.select('.navbar-collapse',true).first();
5395        
5396         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5397             // it's collapsed or collapsing..
5398             return;
5399         }
5400         ce.removeClass('in'); // old...
5401         ce.setHeight(ce.getHeight());
5402         ce.removeClass('show');
5403         ce.addClass('collapsing');
5404         
5405         ce.on('transitionend', function() {
5406             ce.dom.style.height = '';
5407             ce.removeClass('collapsing');
5408             ce.addClass('collapse');
5409         }, this, { single: true} );
5410         ce.setHeight(0);
5411     }
5412     
5413     
5414     
5415 });
5416
5417
5418
5419  
5420
5421  /*
5422  * - LGPL
5423  *
5424  * navbar
5425  * 
5426  */
5427
5428 /**
5429  * @class Roo.bootstrap.NavSimplebar
5430  * @extends Roo.bootstrap.Navbar
5431  * Bootstrap Sidebar class
5432  *
5433  * @cfg {Boolean} inverse is inverted color
5434  * 
5435  * @cfg {String} type (nav | pills | tabs)
5436  * @cfg {Boolean} arrangement stacked | justified
5437  * @cfg {String} align (left | right) alignment
5438  * 
5439  * @cfg {Boolean} main (true|false) main nav bar? default false
5440  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5441  * 
5442  * @cfg {String} tag (header|footer|nav|div) default is nav 
5443
5444  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5445  * 
5446  * 
5447  * @constructor
5448  * Create a new Sidebar
5449  * @param {Object} config The config object
5450  */
5451
5452
5453 Roo.bootstrap.NavSimplebar = function(config){
5454     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5455 };
5456
5457 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5458     
5459     inverse: false,
5460     
5461     type: false,
5462     arrangement: '',
5463     align : false,
5464     
5465     weight : 'light',
5466     
5467     main : false,
5468     
5469     
5470     tag : false,
5471     
5472     
5473     getAutoCreate : function(){
5474         
5475         
5476         var cfg = {
5477             tag : this.tag || 'div',
5478             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5479         };
5480         if (['light','white'].indexOf(this.weight) > -1) {
5481             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5482         }
5483         cfg.cls += ' bg-' + this.weight;
5484         
5485         if (this.inverse) {
5486             cfg.cls += ' navbar-inverse';
5487             
5488         }
5489         
5490         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5491         
5492         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5493             return cfg;
5494         }
5495         
5496         
5497     
5498         
5499         cfg.cn = [
5500             {
5501                 cls: 'nav nav-' + this.xtype,
5502                 tag : 'ul'
5503             }
5504         ];
5505         
5506          
5507         this.type = this.type || 'nav';
5508         if (['tabs','pills'].indexOf(this.type) != -1) {
5509             cfg.cn[0].cls += ' nav-' + this.type
5510         
5511         
5512         } else {
5513             if (this.type!=='nav') {
5514                 Roo.log('nav type must be nav/tabs/pills')
5515             }
5516             cfg.cn[0].cls += ' navbar-nav'
5517         }
5518         
5519         
5520         
5521         
5522         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5523             cfg.cn[0].cls += ' nav-' + this.arrangement;
5524         }
5525         
5526         
5527         if (this.align === 'right') {
5528             cfg.cn[0].cls += ' navbar-right';
5529         }
5530         
5531         
5532         
5533         
5534         return cfg;
5535     
5536         
5537     }
5538     
5539     
5540     
5541 });
5542
5543
5544
5545  
5546
5547  
5548        /*
5549  * - LGPL
5550  *
5551  * navbar
5552  * navbar-fixed-top
5553  * navbar-expand-md  fixed-top 
5554  */
5555
5556 /**
5557  * @class Roo.bootstrap.NavHeaderbar
5558  * @extends Roo.bootstrap.NavSimplebar
5559  * Bootstrap Sidebar class
5560  *
5561  * @cfg {String} brand what is brand
5562  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5563  * @cfg {String} brand_href href of the brand
5564  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5565  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5566  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5567  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5568  * 
5569  * @constructor
5570  * Create a new Sidebar
5571  * @param {Object} config The config object
5572  */
5573
5574
5575 Roo.bootstrap.NavHeaderbar = function(config){
5576     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5577       
5578 };
5579
5580 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5581     
5582     position: '',
5583     brand: '',
5584     brand_href: false,
5585     srButton : true,
5586     autohide : false,
5587     desktopCenter : false,
5588    
5589     
5590     getAutoCreate : function(){
5591         
5592         var   cfg = {
5593             tag: this.nav || 'nav',
5594             cls: 'navbar navbar-expand-md',
5595             role: 'navigation',
5596             cn: []
5597         };
5598         
5599         var cn = cfg.cn;
5600         if (this.desktopCenter) {
5601             cn.push({cls : 'container', cn : []});
5602             cn = cn[0].cn;
5603         }
5604         
5605         if(this.srButton){
5606             var btn = {
5607                 tag: 'button',
5608                 type: 'button',
5609                 cls: 'navbar-toggle navbar-toggler',
5610                 'data-toggle': 'collapse',
5611                 cn: [
5612                     {
5613                         tag: 'span',
5614                         cls: 'sr-only',
5615                         html: 'Toggle navigation'
5616                     },
5617                     {
5618                         tag: 'span',
5619                         cls: 'icon-bar navbar-toggler-icon'
5620                     },
5621                     {
5622                         tag: 'span',
5623                         cls: 'icon-bar'
5624                     },
5625                     {
5626                         tag: 'span',
5627                         cls: 'icon-bar'
5628                     }
5629                 ]
5630             };
5631             
5632             cn.push( Roo.bootstrap.version == 4 ? btn : {
5633                 tag: 'div',
5634                 cls: 'navbar-header',
5635                 cn: [
5636                     btn
5637                 ]
5638             });
5639         }
5640         
5641         cn.push({
5642             tag: 'div',
5643             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5644             cn : []
5645         });
5646         
5647         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5648         
5649         if (['light','white'].indexOf(this.weight) > -1) {
5650             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5651         }
5652         cfg.cls += ' bg-' + this.weight;
5653         
5654         
5655         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5656             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5657             
5658             // tag can override this..
5659             
5660             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5661         }
5662         
5663         if (this.brand !== '') {
5664             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5665             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5666                 tag: 'a',
5667                 href: this.brand_href ? this.brand_href : '#',
5668                 cls: 'navbar-brand',
5669                 cn: [
5670                 this.brand
5671                 ]
5672             });
5673         }
5674         
5675         if(this.main){
5676             cfg.cls += ' main-nav';
5677         }
5678         
5679         
5680         return cfg;
5681
5682         
5683     },
5684     getHeaderChildContainer : function()
5685     {
5686         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5687             return this.el.select('.navbar-header',true).first();
5688         }
5689         
5690         return this.getChildContainer();
5691     },
5692     
5693     getChildContainer : function()
5694     {
5695          
5696         return this.el.select('.roo-navbar-collapse',true).first();
5697          
5698         
5699     },
5700     
5701     initEvents : function()
5702     {
5703         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5704         
5705         if (this.autohide) {
5706             
5707             var prevScroll = 0;
5708             var ft = this.el;
5709             
5710             Roo.get(document).on('scroll',function(e) {
5711                 var ns = Roo.get(document).getScroll().top;
5712                 var os = prevScroll;
5713                 prevScroll = ns;
5714                 
5715                 if(ns > os){
5716                     ft.removeClass('slideDown');
5717                     ft.addClass('slideUp');
5718                     return;
5719                 }
5720                 ft.removeClass('slideUp');
5721                 ft.addClass('slideDown');
5722                  
5723               
5724           },this);
5725         }
5726     }    
5727     
5728 });
5729
5730
5731
5732  
5733
5734  /*
5735  * - LGPL
5736  *
5737  * navbar
5738  * 
5739  */
5740
5741 /**
5742  * @class Roo.bootstrap.NavSidebar
5743  * @extends Roo.bootstrap.Navbar
5744  * Bootstrap Sidebar class
5745  * 
5746  * @constructor
5747  * Create a new Sidebar
5748  * @param {Object} config The config object
5749  */
5750
5751
5752 Roo.bootstrap.NavSidebar = function(config){
5753     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5754 };
5755
5756 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5757     
5758     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5759     
5760     getAutoCreate : function(){
5761         
5762         
5763         return  {
5764             tag: 'div',
5765             cls: 'sidebar sidebar-nav'
5766         };
5767     
5768         
5769     }
5770     
5771     
5772     
5773 });
5774
5775
5776
5777  
5778
5779  /*
5780  * - LGPL
5781  *
5782  * nav group
5783  * 
5784  */
5785
5786 /**
5787  * @class Roo.bootstrap.NavGroup
5788  * @extends Roo.bootstrap.Component
5789  * Bootstrap NavGroup class
5790  * @cfg {String} align (left|right)
5791  * @cfg {Boolean} inverse
5792  * @cfg {String} type (nav|pills|tab) default nav
5793  * @cfg {String} navId - reference Id for navbar.
5794  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5795  * 
5796  * @constructor
5797  * Create a new nav group
5798  * @param {Object} config The config object
5799  */
5800
5801 Roo.bootstrap.NavGroup = function(config){
5802     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5803     this.navItems = [];
5804    
5805     Roo.bootstrap.NavGroup.register(this);
5806      this.addEvents({
5807         /**
5808              * @event changed
5809              * Fires when the active item changes
5810              * @param {Roo.bootstrap.NavGroup} this
5811              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5812              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5813          */
5814         'changed': true
5815      });
5816     
5817 };
5818
5819 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5820     
5821     align: '',
5822     inverse: false,
5823     form: false,
5824     type: 'nav',
5825     navId : '',
5826     // private
5827     pilltype : true,
5828     
5829     navItems : false, 
5830     
5831     getAutoCreate : function()
5832     {
5833         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5834         
5835         cfg = {
5836             tag : 'ul',
5837             cls: 'nav' 
5838         };
5839         if (Roo.bootstrap.version == 4) {
5840             if (['tabs','pills'].indexOf(this.type) != -1) {
5841                 cfg.cls += ' nav-' + this.type; 
5842             } else {
5843                 // trying to remove so header bar can right align top?
5844                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5845                     // do not use on header bar... 
5846                     cfg.cls += ' navbar-nav';
5847                 }
5848             }
5849             
5850         } else {
5851             if (['tabs','pills'].indexOf(this.type) != -1) {
5852                 cfg.cls += ' nav-' + this.type
5853             } else {
5854                 if (this.type !== 'nav') {
5855                     Roo.log('nav type must be nav/tabs/pills')
5856                 }
5857                 cfg.cls += ' navbar-nav'
5858             }
5859         }
5860         
5861         if (this.parent() && this.parent().sidebar) {
5862             cfg = {
5863                 tag: 'ul',
5864                 cls: 'dashboard-menu sidebar-menu'
5865             };
5866             
5867             return cfg;
5868         }
5869         
5870         if (this.form === true) {
5871             cfg = {
5872                 tag: 'form',
5873                 cls: 'navbar-form form-inline'
5874             };
5875             //nav navbar-right ml-md-auto
5876             if (this.align === 'right') {
5877                 cfg.cls += ' navbar-right ml-md-auto';
5878             } else {
5879                 cfg.cls += ' navbar-left';
5880             }
5881         }
5882         
5883         if (this.align === 'right') {
5884             cfg.cls += ' navbar-right ml-md-auto';
5885         } else {
5886             cfg.cls += ' mr-auto';
5887         }
5888         
5889         if (this.inverse) {
5890             cfg.cls += ' navbar-inverse';
5891             
5892         }
5893         
5894         
5895         return cfg;
5896     },
5897     /**
5898     * sets the active Navigation item
5899     * @param {Roo.bootstrap.NavItem} the new current navitem
5900     */
5901     setActiveItem : function(item)
5902     {
5903         var prev = false;
5904         Roo.each(this.navItems, function(v){
5905             if (v == item) {
5906                 return ;
5907             }
5908             if (v.isActive()) {
5909                 v.setActive(false, true);
5910                 prev = v;
5911                 
5912             }
5913             
5914         });
5915
5916         item.setActive(true, true);
5917         this.fireEvent('changed', this, item, prev);
5918         
5919         
5920     },
5921     /**
5922     * gets the active Navigation item
5923     * @return {Roo.bootstrap.NavItem} the current navitem
5924     */
5925     getActive : function()
5926     {
5927         
5928         var prev = false;
5929         Roo.each(this.navItems, function(v){
5930             
5931             if (v.isActive()) {
5932                 prev = v;
5933                 
5934             }
5935             
5936         });
5937         return prev;
5938     },
5939     
5940     indexOfNav : function()
5941     {
5942         
5943         var prev = false;
5944         Roo.each(this.navItems, function(v,i){
5945             
5946             if (v.isActive()) {
5947                 prev = i;
5948                 
5949             }
5950             
5951         });
5952         return prev;
5953     },
5954     /**
5955     * adds a Navigation item
5956     * @param {Roo.bootstrap.NavItem} the navitem to add
5957     */
5958     addItem : function(cfg)
5959     {
5960         if (this.form && Roo.bootstrap.version == 4) {
5961             cfg.tag = 'div';
5962         }
5963         var cn = new Roo.bootstrap.NavItem(cfg);
5964         this.register(cn);
5965         cn.parentId = this.id;
5966         cn.onRender(this.el, null);
5967         return cn;
5968     },
5969     /**
5970     * register a Navigation item
5971     * @param {Roo.bootstrap.NavItem} the navitem to add
5972     */
5973     register : function(item)
5974     {
5975         this.navItems.push( item);
5976         item.navId = this.navId;
5977     
5978     },
5979     
5980     /**
5981     * clear all the Navigation item
5982     */
5983    
5984     clearAll : function()
5985     {
5986         this.navItems = [];
5987         this.el.dom.innerHTML = '';
5988     },
5989     
5990     getNavItem: function(tabId)
5991     {
5992         var ret = false;
5993         Roo.each(this.navItems, function(e) {
5994             if (e.tabId == tabId) {
5995                ret =  e;
5996                return false;
5997             }
5998             return true;
5999             
6000         });
6001         return ret;
6002     },
6003     
6004     setActiveNext : function()
6005     {
6006         var i = this.indexOfNav(this.getActive());
6007         if (i > this.navItems.length) {
6008             return;
6009         }
6010         this.setActiveItem(this.navItems[i+1]);
6011     },
6012     setActivePrev : function()
6013     {
6014         var i = this.indexOfNav(this.getActive());
6015         if (i  < 1) {
6016             return;
6017         }
6018         this.setActiveItem(this.navItems[i-1]);
6019     },
6020     clearWasActive : function(except) {
6021         Roo.each(this.navItems, function(e) {
6022             if (e.tabId != except.tabId && e.was_active) {
6023                e.was_active = false;
6024                return false;
6025             }
6026             return true;
6027             
6028         });
6029     },
6030     getWasActive : function ()
6031     {
6032         var r = false;
6033         Roo.each(this.navItems, function(e) {
6034             if (e.was_active) {
6035                r = e;
6036                return false;
6037             }
6038             return true;
6039             
6040         });
6041         return r;
6042     }
6043     
6044     
6045 });
6046
6047  
6048 Roo.apply(Roo.bootstrap.NavGroup, {
6049     
6050     groups: {},
6051      /**
6052     * register a Navigation Group
6053     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6054     */
6055     register : function(navgrp)
6056     {
6057         this.groups[navgrp.navId] = navgrp;
6058         
6059     },
6060     /**
6061     * fetch a Navigation Group based on the navigation ID
6062     * @param {string} the navgroup to add
6063     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6064     */
6065     get: function(navId) {
6066         if (typeof(this.groups[navId]) == 'undefined') {
6067             return false;
6068             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6069         }
6070         return this.groups[navId] ;
6071     }
6072     
6073     
6074     
6075 });
6076
6077  /*
6078  * - LGPL
6079  *
6080  * row
6081  * 
6082  */
6083
6084 /**
6085  * @class Roo.bootstrap.NavItem
6086  * @extends Roo.bootstrap.Component
6087  * Bootstrap Navbar.NavItem class
6088  * @cfg {String} href  link to
6089  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6090  * @cfg {Boolean} button_outline show and outlined button
6091  * @cfg {String} html content of button
6092  * @cfg {String} badge text inside badge
6093  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6094  * @cfg {String} glyphicon DEPRICATED - use fa
6095  * @cfg {String} icon DEPRICATED - use fa
6096  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6097  * @cfg {Boolean} active Is item active
6098  * @cfg {Boolean} disabled Is item disabled
6099  * @cfg {String} linkcls  Link Class
6100  * @cfg {Boolean} preventDefault (true | false) default false
6101  * @cfg {String} tabId the tab that this item activates.
6102  * @cfg {String} tagtype (a|span) render as a href or span?
6103  * @cfg {Boolean} animateRef (true|false) link to element default false  
6104   
6105  * @constructor
6106  * Create a new Navbar Item
6107  * @param {Object} config The config object
6108  */
6109 Roo.bootstrap.NavItem = function(config){
6110     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6111     this.addEvents({
6112         // raw events
6113         /**
6114          * @event click
6115          * The raw click event for the entire grid.
6116          * @param {Roo.EventObject} e
6117          */
6118         "click" : true,
6119          /**
6120             * @event changed
6121             * Fires when the active item active state changes
6122             * @param {Roo.bootstrap.NavItem} this
6123             * @param {boolean} state the new state
6124              
6125          */
6126         'changed': true,
6127         /**
6128             * @event scrollto
6129             * Fires when scroll to element
6130             * @param {Roo.bootstrap.NavItem} this
6131             * @param {Object} options
6132             * @param {Roo.EventObject} e
6133              
6134          */
6135         'scrollto': true
6136     });
6137    
6138 };
6139
6140 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6141     
6142     href: false,
6143     html: '',
6144     badge: '',
6145     icon: false,
6146     fa : false,
6147     glyphicon: false,
6148     active: false,
6149     preventDefault : false,
6150     tabId : false,
6151     tagtype : 'a',
6152     tag: 'li',
6153     disabled : false,
6154     animateRef : false,
6155     was_active : false,
6156     button_weight : '',
6157     button_outline : false,
6158     linkcls : '',
6159     navLink: false,
6160     
6161     getAutoCreate : function(){
6162          
6163         var cfg = {
6164             tag: this.tag,
6165             cls: 'nav-item'
6166         };
6167         
6168         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6169         
6170         if (this.active) {
6171             cfg.cls +=  ' active' ;
6172         }
6173         if (this.disabled) {
6174             cfg.cls += ' disabled';
6175         }
6176         
6177         // BS4 only?
6178         if (this.button_weight.length) {
6179             cfg.tag = this.href ? 'a' : 'button';
6180             cfg.html = this.html || '';
6181             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6182             if (this.href) {
6183                 cfg.href = this.href;
6184             }
6185             if (this.fa) {
6186                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6187             }
6188             
6189             // menu .. should add dropdown-menu class - so no need for carat..
6190             
6191             if (this.badge !== '') {
6192                  
6193                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6194             }
6195             return cfg;
6196         }
6197         
6198         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6199             cfg.cn = [
6200                 {
6201                     tag: this.tagtype,
6202                     href : this.href || "#",
6203                     html: this.html || ''
6204                 }
6205             ];
6206             if (this.tagtype == 'a') {
6207                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6208         
6209             }
6210             if (this.icon) {
6211                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6212             }
6213             if (this.fa) {
6214                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6215             }
6216             if(this.glyphicon) {
6217                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6218             }
6219             
6220             if (this.menu) {
6221                 
6222                 cfg.cn[0].html += " <span class='caret'></span>";
6223              
6224             }
6225             
6226             if (this.badge !== '') {
6227                  
6228                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6229             }
6230         }
6231         
6232         
6233         
6234         return cfg;
6235     },
6236     onRender : function(ct, position)
6237     {
6238        // Roo.log("Call onRender: " + this.xtype);
6239         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6240             this.tag = 'div';
6241         }
6242         
6243         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6244         this.navLink = this.el.select('.nav-link',true).first();
6245         return ret;
6246     },
6247       
6248     
6249     initEvents: function() 
6250     {
6251         if (typeof (this.menu) != 'undefined') {
6252             this.menu.parentType = this.xtype;
6253             this.menu.triggerEl = this.el;
6254             this.menu = this.addxtype(Roo.apply({}, this.menu));
6255         }
6256         
6257         this.el.on('click', this.onClick, this);
6258         
6259         //if(this.tagtype == 'span'){
6260         //    this.el.select('span',true).on('click', this.onClick, this);
6261         //}
6262        
6263         // at this point parent should be available..
6264         this.parent().register(this);
6265     },
6266     
6267     onClick : function(e)
6268     {
6269         if (e.getTarget('.dropdown-menu-item')) {
6270             // did you click on a menu itemm.... - then don't trigger onclick..
6271             return;
6272         }
6273         
6274         if(
6275                 this.preventDefault || 
6276                 this.href == '#' 
6277         ){
6278             Roo.log("NavItem - prevent Default?");
6279             e.preventDefault();
6280         }
6281         
6282         if (this.disabled) {
6283             return;
6284         }
6285         
6286         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6287         if (tg && tg.transition) {
6288             Roo.log("waiting for the transitionend");
6289             return;
6290         }
6291         
6292         
6293         
6294         //Roo.log("fire event clicked");
6295         if(this.fireEvent('click', this, e) === false){
6296             return;
6297         };
6298         
6299         if(this.tagtype == 'span'){
6300             return;
6301         }
6302         
6303         //Roo.log(this.href);
6304         var ael = this.el.select('a',true).first();
6305         //Roo.log(ael);
6306         
6307         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6308             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6309             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6310                 return; // ignore... - it's a 'hash' to another page.
6311             }
6312             Roo.log("NavItem - prevent Default?");
6313             e.preventDefault();
6314             this.scrollToElement(e);
6315         }
6316         
6317         
6318         var p =  this.parent();
6319    
6320         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6321             if (typeof(p.setActiveItem) !== 'undefined') {
6322                 p.setActiveItem(this);
6323             }
6324         }
6325         
6326         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6327         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6328             // remove the collapsed menu expand...
6329             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6330         }
6331     },
6332     
6333     isActive: function () {
6334         return this.active
6335     },
6336     setActive : function(state, fire, is_was_active)
6337     {
6338         if (this.active && !state && this.navId) {
6339             this.was_active = true;
6340             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6341             if (nv) {
6342                 nv.clearWasActive(this);
6343             }
6344             
6345         }
6346         this.active = state;
6347         
6348         if (!state ) {
6349             this.el.removeClass('active');
6350             this.navLink ? this.navLink.removeClass('active') : false;
6351         } else if (!this.el.hasClass('active')) {
6352             
6353             this.el.addClass('active');
6354             if (Roo.bootstrap.version == 4 && this.navLink ) {
6355                 this.navLink.addClass('active');
6356             }
6357             
6358         }
6359         if (fire) {
6360             this.fireEvent('changed', this, state);
6361         }
6362         
6363         // show a panel if it's registered and related..
6364         
6365         if (!this.navId || !this.tabId || !state || is_was_active) {
6366             return;
6367         }
6368         
6369         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6370         if (!tg) {
6371             return;
6372         }
6373         var pan = tg.getPanelByName(this.tabId);
6374         if (!pan) {
6375             return;
6376         }
6377         // if we can not flip to new panel - go back to old nav highlight..
6378         if (false == tg.showPanel(pan)) {
6379             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6380             if (nv) {
6381                 var onav = nv.getWasActive();
6382                 if (onav) {
6383                     onav.setActive(true, false, true);
6384                 }
6385             }
6386             
6387         }
6388         
6389         
6390         
6391     },
6392      // this should not be here...
6393     setDisabled : function(state)
6394     {
6395         this.disabled = state;
6396         if (!state ) {
6397             this.el.removeClass('disabled');
6398         } else if (!this.el.hasClass('disabled')) {
6399             this.el.addClass('disabled');
6400         }
6401         
6402     },
6403     
6404     /**
6405      * Fetch the element to display the tooltip on.
6406      * @return {Roo.Element} defaults to this.el
6407      */
6408     tooltipEl : function()
6409     {
6410         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6411     },
6412     
6413     scrollToElement : function(e)
6414     {
6415         var c = document.body;
6416         
6417         /*
6418          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6419          */
6420         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6421             c = document.documentElement;
6422         }
6423         
6424         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6425         
6426         if(!target){
6427             return;
6428         }
6429
6430         var o = target.calcOffsetsTo(c);
6431         
6432         var options = {
6433             target : target,
6434             value : o[1]
6435         };
6436         
6437         this.fireEvent('scrollto', this, options, e);
6438         
6439         Roo.get(c).scrollTo('top', options.value, true);
6440         
6441         return;
6442     }
6443 });
6444  
6445
6446  /*
6447  * - LGPL
6448  *
6449  * sidebar item
6450  *
6451  *  li
6452  *    <span> icon </span>
6453  *    <span> text </span>
6454  *    <span>badge </span>
6455  */
6456
6457 /**
6458  * @class Roo.bootstrap.NavSidebarItem
6459  * @extends Roo.bootstrap.NavItem
6460  * Bootstrap Navbar.NavSidebarItem class
6461  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6462  * {Boolean} open is the menu open
6463  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6464  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6465  * {String} buttonSize (sm|md|lg)the extra classes for the button
6466  * {Boolean} showArrow show arrow next to the text (default true)
6467  * @constructor
6468  * Create a new Navbar Button
6469  * @param {Object} config The config object
6470  */
6471 Roo.bootstrap.NavSidebarItem = function(config){
6472     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6473     this.addEvents({
6474         // raw events
6475         /**
6476          * @event click
6477          * The raw click event for the entire grid.
6478          * @param {Roo.EventObject} e
6479          */
6480         "click" : true,
6481          /**
6482             * @event changed
6483             * Fires when the active item active state changes
6484             * @param {Roo.bootstrap.NavSidebarItem} this
6485             * @param {boolean} state the new state
6486              
6487          */
6488         'changed': true
6489     });
6490    
6491 };
6492
6493 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6494     
6495     badgeWeight : 'default',
6496     
6497     open: false,
6498     
6499     buttonView : false,
6500     
6501     buttonWeight : 'default',
6502     
6503     buttonSize : 'md',
6504     
6505     showArrow : true,
6506     
6507     getAutoCreate : function(){
6508         
6509         
6510         var a = {
6511                 tag: 'a',
6512                 href : this.href || '#',
6513                 cls: '',
6514                 html : '',
6515                 cn : []
6516         };
6517         
6518         if(this.buttonView){
6519             a = {
6520                 tag: 'button',
6521                 href : this.href || '#',
6522                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6523                 html : this.html,
6524                 cn : []
6525             };
6526         }
6527         
6528         var cfg = {
6529             tag: 'li',
6530             cls: '',
6531             cn: [ a ]
6532         };
6533         
6534         if (this.active) {
6535             cfg.cls += ' active';
6536         }
6537         
6538         if (this.disabled) {
6539             cfg.cls += ' disabled';
6540         }
6541         if (this.open) {
6542             cfg.cls += ' open x-open';
6543         }
6544         // left icon..
6545         if (this.glyphicon || this.icon) {
6546             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6547             a.cn.push({ tag : 'i', cls : c }) ;
6548         }
6549         
6550         if(!this.buttonView){
6551             var span = {
6552                 tag: 'span',
6553                 html : this.html || ''
6554             };
6555
6556             a.cn.push(span);
6557             
6558         }
6559         
6560         if (this.badge !== '') {
6561             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6562         }
6563         
6564         if (this.menu) {
6565             
6566             if(this.showArrow){
6567                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6568             }
6569             
6570             a.cls += ' dropdown-toggle treeview' ;
6571         }
6572         
6573         return cfg;
6574     },
6575     
6576     initEvents : function()
6577     { 
6578         if (typeof (this.menu) != 'undefined') {
6579             this.menu.parentType = this.xtype;
6580             this.menu.triggerEl = this.el;
6581             this.menu = this.addxtype(Roo.apply({}, this.menu));
6582         }
6583         
6584         this.el.on('click', this.onClick, this);
6585         
6586         if(this.badge !== ''){
6587             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6588         }
6589         
6590     },
6591     
6592     onClick : function(e)
6593     {
6594         if(this.disabled){
6595             e.preventDefault();
6596             return;
6597         }
6598         
6599         if(this.preventDefault){
6600             e.preventDefault();
6601         }
6602         
6603         this.fireEvent('click', this, e);
6604     },
6605     
6606     disable : function()
6607     {
6608         this.setDisabled(true);
6609     },
6610     
6611     enable : function()
6612     {
6613         this.setDisabled(false);
6614     },
6615     
6616     setDisabled : function(state)
6617     {
6618         if(this.disabled == state){
6619             return;
6620         }
6621         
6622         this.disabled = state;
6623         
6624         if (state) {
6625             this.el.addClass('disabled');
6626             return;
6627         }
6628         
6629         this.el.removeClass('disabled');
6630         
6631         return;
6632     },
6633     
6634     setActive : function(state)
6635     {
6636         if(this.active == state){
6637             return;
6638         }
6639         
6640         this.active = state;
6641         
6642         if (state) {
6643             this.el.addClass('active');
6644             return;
6645         }
6646         
6647         this.el.removeClass('active');
6648         
6649         return;
6650     },
6651     
6652     isActive: function () 
6653     {
6654         return this.active;
6655     },
6656     
6657     setBadge : function(str)
6658     {
6659         if(!this.badgeEl){
6660             return;
6661         }
6662         
6663         this.badgeEl.dom.innerHTML = str;
6664     }
6665     
6666    
6667      
6668  
6669 });
6670  
6671
6672  /*
6673  * - LGPL
6674  *
6675  *  Breadcrumb Nav
6676  * 
6677  */
6678 Roo.namespace('Roo.bootstrap.breadcrumb');
6679
6680
6681 /**
6682  * @class Roo.bootstrap.breadcrumb.Nav
6683  * @extends Roo.bootstrap.Component
6684  * Bootstrap Breadcrumb Nav Class
6685  *  
6686  * @children Roo.bootstrap.breadcrumb.Item
6687  * 
6688  * @constructor
6689  * Create a new breadcrumb.Nav
6690  * @param {Object} config The config object
6691  */
6692
6693
6694 Roo.bootstrap.breadcrumb.Nav = function(config){
6695     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6696     
6697     
6698 };
6699
6700 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6701     
6702     getAutoCreate : function()
6703     {
6704
6705         var cfg = {
6706             tag: 'nav',
6707             cn : [
6708                 {
6709                     tag : 'ol',
6710                     cls : 'breadcrumb'
6711                 }
6712             ]
6713             
6714         };
6715           
6716         return cfg;
6717     },
6718     
6719     initEvents: function()
6720     {
6721         this.olEl = this.el.select('ol',true).first();    
6722     },
6723     getChildContainer : function()
6724     {
6725         return this.olEl;  
6726     }
6727     
6728 });
6729
6730  /*
6731  * - LGPL
6732  *
6733  *  Breadcrumb Item
6734  * 
6735  */
6736
6737
6738 /**
6739  * @class Roo.bootstrap.breadcrumb.Nav
6740  * @extends Roo.bootstrap.Component
6741  * Bootstrap Breadcrumb Nav Class
6742  *  
6743  * @children Roo.bootstrap.breadcrumb.Component
6744  * @cfg {String} html the content of the link.
6745  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6746  * @cfg {Boolean} active is it active
6747
6748  * 
6749  * @constructor
6750  * Create a new breadcrumb.Nav
6751  * @param {Object} config The config object
6752  */
6753
6754 Roo.bootstrap.breadcrumb.Item = function(config){
6755     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6756     this.addEvents({
6757         // img events
6758         /**
6759          * @event click
6760          * The img click event for the img.
6761          * @param {Roo.EventObject} e
6762          */
6763         "click" : true
6764     });
6765     
6766 };
6767
6768 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6769     
6770     href: false,
6771     html : '',
6772     
6773     getAutoCreate : function()
6774     {
6775
6776         var cfg = {
6777             tag: 'li',
6778             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6779         };
6780         if (this.href !== false) {
6781             cfg.cn = [{
6782                 tag : 'a',
6783                 href : this.href,
6784                 html : this.html
6785             }];
6786         } else {
6787             cfg.html = this.html;
6788         }
6789         
6790         return cfg;
6791     },
6792     
6793     initEvents: function()
6794     {
6795         if (this.href) {
6796             this.el.select('a', true).first().on('click',this.onClick, this)
6797         }
6798         
6799     },
6800     onClick : function(e)
6801     {
6802         e.preventDefault();
6803         this.fireEvent('click',this,  e);
6804     }
6805     
6806 });
6807
6808  /*
6809  * - LGPL
6810  *
6811  * row
6812  * 
6813  */
6814
6815 /**
6816  * @class Roo.bootstrap.Row
6817  * @extends Roo.bootstrap.Component
6818  * Bootstrap Row class (contains columns...)
6819  * 
6820  * @constructor
6821  * Create a new Row
6822  * @param {Object} config The config object
6823  */
6824
6825 Roo.bootstrap.Row = function(config){
6826     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6827 };
6828
6829 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6830     
6831     getAutoCreate : function(){
6832        return {
6833             cls: 'row clearfix'
6834        };
6835     }
6836     
6837     
6838 });
6839
6840  
6841
6842  /*
6843  * - LGPL
6844  *
6845  * pagination
6846  * 
6847  */
6848
6849 /**
6850  * @class Roo.bootstrap.Pagination
6851  * @extends Roo.bootstrap.Component
6852  * Bootstrap Pagination class
6853  * @cfg {String} size xs | sm | md | lg
6854  * @cfg {Boolean} inverse false | true
6855  * 
6856  * @constructor
6857  * Create a new Pagination
6858  * @param {Object} config The config object
6859  */
6860
6861 Roo.bootstrap.Pagination = function(config){
6862     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6863 };
6864
6865 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6866     
6867     cls: false,
6868     size: false,
6869     inverse: false,
6870     
6871     getAutoCreate : function(){
6872         var cfg = {
6873             tag: 'ul',
6874                 cls: 'pagination'
6875         };
6876         if (this.inverse) {
6877             cfg.cls += ' inverse';
6878         }
6879         if (this.html) {
6880             cfg.html=this.html;
6881         }
6882         if (this.cls) {
6883             cfg.cls += " " + this.cls;
6884         }
6885         return cfg;
6886     }
6887    
6888 });
6889
6890  
6891
6892  /*
6893  * - LGPL
6894  *
6895  * Pagination item
6896  * 
6897  */
6898
6899
6900 /**
6901  * @class Roo.bootstrap.PaginationItem
6902  * @extends Roo.bootstrap.Component
6903  * Bootstrap PaginationItem class
6904  * @cfg {String} html text
6905  * @cfg {String} href the link
6906  * @cfg {Boolean} preventDefault (true | false) default true
6907  * @cfg {Boolean} active (true | false) default false
6908  * @cfg {Boolean} disabled default false
6909  * 
6910  * 
6911  * @constructor
6912  * Create a new PaginationItem
6913  * @param {Object} config The config object
6914  */
6915
6916
6917 Roo.bootstrap.PaginationItem = function(config){
6918     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6919     this.addEvents({
6920         // raw events
6921         /**
6922          * @event click
6923          * The raw click event for the entire grid.
6924          * @param {Roo.EventObject} e
6925          */
6926         "click" : true
6927     });
6928 };
6929
6930 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6931     
6932     href : false,
6933     html : false,
6934     preventDefault: true,
6935     active : false,
6936     cls : false,
6937     disabled: false,
6938     
6939     getAutoCreate : function(){
6940         var cfg= {
6941             tag: 'li',
6942             cn: [
6943                 {
6944                     tag : 'a',
6945                     href : this.href ? this.href : '#',
6946                     html : this.html ? this.html : ''
6947                 }
6948             ]
6949         };
6950         
6951         if(this.cls){
6952             cfg.cls = this.cls;
6953         }
6954         
6955         if(this.disabled){
6956             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6957         }
6958         
6959         if(this.active){
6960             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6961         }
6962         
6963         return cfg;
6964     },
6965     
6966     initEvents: function() {
6967         
6968         this.el.on('click', this.onClick, this);
6969         
6970     },
6971     onClick : function(e)
6972     {
6973         Roo.log('PaginationItem on click ');
6974         if(this.preventDefault){
6975             e.preventDefault();
6976         }
6977         
6978         if(this.disabled){
6979             return;
6980         }
6981         
6982         this.fireEvent('click', this, e);
6983     }
6984    
6985 });
6986
6987  
6988
6989  /*
6990  * - LGPL
6991  *
6992  * slider
6993  * 
6994  */
6995
6996
6997 /**
6998  * @class Roo.bootstrap.Slider
6999  * @extends Roo.bootstrap.Component
7000  * Bootstrap Slider class
7001  *    
7002  * @constructor
7003  * Create a new Slider
7004  * @param {Object} config The config object
7005  */
7006
7007 Roo.bootstrap.Slider = function(config){
7008     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7009 };
7010
7011 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7012     
7013     getAutoCreate : function(){
7014         
7015         var cfg = {
7016             tag: 'div',
7017             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7018             cn: [
7019                 {
7020                     tag: 'a',
7021                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7022                 }
7023             ]
7024         };
7025         
7026         return cfg;
7027     }
7028    
7029 });
7030
7031  /*
7032  * Based on:
7033  * Ext JS Library 1.1.1
7034  * Copyright(c) 2006-2007, Ext JS, LLC.
7035  *
7036  * Originally Released Under LGPL - original licence link has changed is not relivant.
7037  *
7038  * Fork - LGPL
7039  * <script type="text/javascript">
7040  */
7041  
7042
7043 /**
7044  * @class Roo.grid.ColumnModel
7045  * @extends Roo.util.Observable
7046  * This is the default implementation of a ColumnModel used by the Grid. It defines
7047  * the columns in the grid.
7048  * <br>Usage:<br>
7049  <pre><code>
7050  var colModel = new Roo.grid.ColumnModel([
7051         {header: "Ticker", width: 60, sortable: true, locked: true},
7052         {header: "Company Name", width: 150, sortable: true},
7053         {header: "Market Cap.", width: 100, sortable: true},
7054         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7055         {header: "Employees", width: 100, sortable: true, resizable: false}
7056  ]);
7057  </code></pre>
7058  * <p>
7059  
7060  * The config options listed for this class are options which may appear in each
7061  * individual column definition.
7062  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7063  * @constructor
7064  * @param {Object} config An Array of column config objects. See this class's
7065  * config objects for details.
7066 */
7067 Roo.grid.ColumnModel = function(config){
7068         /**
7069      * The config passed into the constructor
7070      */
7071     this.config = config;
7072     this.lookup = {};
7073
7074     // if no id, create one
7075     // if the column does not have a dataIndex mapping,
7076     // map it to the order it is in the config
7077     for(var i = 0, len = config.length; i < len; i++){
7078         var c = config[i];
7079         if(typeof c.dataIndex == "undefined"){
7080             c.dataIndex = i;
7081         }
7082         if(typeof c.renderer == "string"){
7083             c.renderer = Roo.util.Format[c.renderer];
7084         }
7085         if(typeof c.id == "undefined"){
7086             c.id = Roo.id();
7087         }
7088         if(c.editor && c.editor.xtype){
7089             c.editor  = Roo.factory(c.editor, Roo.grid);
7090         }
7091         if(c.editor && c.editor.isFormField){
7092             c.editor = new Roo.grid.GridEditor(c.editor);
7093         }
7094         this.lookup[c.id] = c;
7095     }
7096
7097     /**
7098      * The width of columns which have no width specified (defaults to 100)
7099      * @type Number
7100      */
7101     this.defaultWidth = 100;
7102
7103     /**
7104      * Default sortable of columns which have no sortable specified (defaults to false)
7105      * @type Boolean
7106      */
7107     this.defaultSortable = false;
7108
7109     this.addEvents({
7110         /**
7111              * @event widthchange
7112              * Fires when the width of a column changes.
7113              * @param {ColumnModel} this
7114              * @param {Number} columnIndex The column index
7115              * @param {Number} newWidth The new width
7116              */
7117             "widthchange": true,
7118         /**
7119              * @event headerchange
7120              * Fires when the text of a header changes.
7121              * @param {ColumnModel} this
7122              * @param {Number} columnIndex The column index
7123              * @param {Number} newText The new header text
7124              */
7125             "headerchange": true,
7126         /**
7127              * @event hiddenchange
7128              * Fires when a column is hidden or "unhidden".
7129              * @param {ColumnModel} this
7130              * @param {Number} columnIndex The column index
7131              * @param {Boolean} hidden true if hidden, false otherwise
7132              */
7133             "hiddenchange": true,
7134             /**
7135          * @event columnmoved
7136          * Fires when a column is moved.
7137          * @param {ColumnModel} this
7138          * @param {Number} oldIndex
7139          * @param {Number} newIndex
7140          */
7141         "columnmoved" : true,
7142         /**
7143          * @event columlockchange
7144          * Fires when a column's locked state is changed
7145          * @param {ColumnModel} this
7146          * @param {Number} colIndex
7147          * @param {Boolean} locked true if locked
7148          */
7149         "columnlockchange" : true
7150     });
7151     Roo.grid.ColumnModel.superclass.constructor.call(this);
7152 };
7153 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7154     /**
7155      * @cfg {String} header The header text to display in the Grid view.
7156      */
7157     /**
7158      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7159      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7160      * specified, the column's index is used as an index into the Record's data Array.
7161      */
7162     /**
7163      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7164      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7165      */
7166     /**
7167      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7168      * Defaults to the value of the {@link #defaultSortable} property.
7169      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7170      */
7171     /**
7172      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7173      */
7174     /**
7175      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7176      */
7177     /**
7178      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7179      */
7180     /**
7181      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7182      */
7183     /**
7184      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7185      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7186      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7187      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7188      */
7189        /**
7190      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7191      */
7192     /**
7193      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7194      */
7195     /**
7196      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7197      */
7198     /**
7199      * @cfg {String} cursor (Optional)
7200      */
7201     /**
7202      * @cfg {String} tooltip (Optional)
7203      */
7204     /**
7205      * @cfg {Number} xs (Optional)
7206      */
7207     /**
7208      * @cfg {Number} sm (Optional)
7209      */
7210     /**
7211      * @cfg {Number} md (Optional)
7212      */
7213     /**
7214      * @cfg {Number} lg (Optional)
7215      */
7216     /**
7217      * Returns the id of the column at the specified index.
7218      * @param {Number} index The column index
7219      * @return {String} the id
7220      */
7221     getColumnId : function(index){
7222         return this.config[index].id;
7223     },
7224
7225     /**
7226      * Returns the column for a specified id.
7227      * @param {String} id The column id
7228      * @return {Object} the column
7229      */
7230     getColumnById : function(id){
7231         return this.lookup[id];
7232     },
7233
7234     
7235     /**
7236      * Returns the column for a specified dataIndex.
7237      * @param {String} dataIndex The column dataIndex
7238      * @return {Object|Boolean} the column or false if not found
7239      */
7240     getColumnByDataIndex: function(dataIndex){
7241         var index = this.findColumnIndex(dataIndex);
7242         return index > -1 ? this.config[index] : false;
7243     },
7244     
7245     /**
7246      * Returns the index for a specified column id.
7247      * @param {String} id The column id
7248      * @return {Number} the index, or -1 if not found
7249      */
7250     getIndexById : function(id){
7251         for(var i = 0, len = this.config.length; i < len; i++){
7252             if(this.config[i].id == id){
7253                 return i;
7254             }
7255         }
7256         return -1;
7257     },
7258     
7259     /**
7260      * Returns the index for a specified column dataIndex.
7261      * @param {String} dataIndex The column dataIndex
7262      * @return {Number} the index, or -1 if not found
7263      */
7264     
7265     findColumnIndex : function(dataIndex){
7266         for(var i = 0, len = this.config.length; i < len; i++){
7267             if(this.config[i].dataIndex == dataIndex){
7268                 return i;
7269             }
7270         }
7271         return -1;
7272     },
7273     
7274     
7275     moveColumn : function(oldIndex, newIndex){
7276         var c = this.config[oldIndex];
7277         this.config.splice(oldIndex, 1);
7278         this.config.splice(newIndex, 0, c);
7279         this.dataMap = null;
7280         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7281     },
7282
7283     isLocked : function(colIndex){
7284         return this.config[colIndex].locked === true;
7285     },
7286
7287     setLocked : function(colIndex, value, suppressEvent){
7288         if(this.isLocked(colIndex) == value){
7289             return;
7290         }
7291         this.config[colIndex].locked = value;
7292         if(!suppressEvent){
7293             this.fireEvent("columnlockchange", this, colIndex, value);
7294         }
7295     },
7296
7297     getTotalLockedWidth : function(){
7298         var totalWidth = 0;
7299         for(var i = 0; i < this.config.length; i++){
7300             if(this.isLocked(i) && !this.isHidden(i)){
7301                 this.totalWidth += this.getColumnWidth(i);
7302             }
7303         }
7304         return totalWidth;
7305     },
7306
7307     getLockedCount : function(){
7308         for(var i = 0, len = this.config.length; i < len; i++){
7309             if(!this.isLocked(i)){
7310                 return i;
7311             }
7312         }
7313         
7314         return this.config.length;
7315     },
7316
7317     /**
7318      * Returns the number of columns.
7319      * @return {Number}
7320      */
7321     getColumnCount : function(visibleOnly){
7322         if(visibleOnly === true){
7323             var c = 0;
7324             for(var i = 0, len = this.config.length; i < len; i++){
7325                 if(!this.isHidden(i)){
7326                     c++;
7327                 }
7328             }
7329             return c;
7330         }
7331         return this.config.length;
7332     },
7333
7334     /**
7335      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7336      * @param {Function} fn
7337      * @param {Object} scope (optional)
7338      * @return {Array} result
7339      */
7340     getColumnsBy : function(fn, scope){
7341         var r = [];
7342         for(var i = 0, len = this.config.length; i < len; i++){
7343             var c = this.config[i];
7344             if(fn.call(scope||this, c, i) === true){
7345                 r[r.length] = c;
7346             }
7347         }
7348         return r;
7349     },
7350
7351     /**
7352      * Returns true if the specified column is sortable.
7353      * @param {Number} col The column index
7354      * @return {Boolean}
7355      */
7356     isSortable : function(col){
7357         if(typeof this.config[col].sortable == "undefined"){
7358             return this.defaultSortable;
7359         }
7360         return this.config[col].sortable;
7361     },
7362
7363     /**
7364      * Returns the rendering (formatting) function defined for the column.
7365      * @param {Number} col The column index.
7366      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7367      */
7368     getRenderer : function(col){
7369         if(!this.config[col].renderer){
7370             return Roo.grid.ColumnModel.defaultRenderer;
7371         }
7372         return this.config[col].renderer;
7373     },
7374
7375     /**
7376      * Sets the rendering (formatting) function for a column.
7377      * @param {Number} col The column index
7378      * @param {Function} fn The function to use to process the cell's raw data
7379      * to return HTML markup for the grid view. The render function is called with
7380      * the following parameters:<ul>
7381      * <li>Data value.</li>
7382      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7383      * <li>css A CSS style string to apply to the table cell.</li>
7384      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7385      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7386      * <li>Row index</li>
7387      * <li>Column index</li>
7388      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7389      */
7390     setRenderer : function(col, fn){
7391         this.config[col].renderer = fn;
7392     },
7393
7394     /**
7395      * Returns the width for the specified column.
7396      * @param {Number} col The column index
7397      * @return {Number}
7398      */
7399     getColumnWidth : function(col){
7400         return this.config[col].width * 1 || this.defaultWidth;
7401     },
7402
7403     /**
7404      * Sets the width for a column.
7405      * @param {Number} col The column index
7406      * @param {Number} width The new width
7407      */
7408     setColumnWidth : function(col, width, suppressEvent){
7409         this.config[col].width = width;
7410         this.totalWidth = null;
7411         if(!suppressEvent){
7412              this.fireEvent("widthchange", this, col, width);
7413         }
7414     },
7415
7416     /**
7417      * Returns the total width of all columns.
7418      * @param {Boolean} includeHidden True to include hidden column widths
7419      * @return {Number}
7420      */
7421     getTotalWidth : function(includeHidden){
7422         if(!this.totalWidth){
7423             this.totalWidth = 0;
7424             for(var i = 0, len = this.config.length; i < len; i++){
7425                 if(includeHidden || !this.isHidden(i)){
7426                     this.totalWidth += this.getColumnWidth(i);
7427                 }
7428             }
7429         }
7430         return this.totalWidth;
7431     },
7432
7433     /**
7434      * Returns the header for the specified column.
7435      * @param {Number} col The column index
7436      * @return {String}
7437      */
7438     getColumnHeader : function(col){
7439         return this.config[col].header;
7440     },
7441
7442     /**
7443      * Sets the header for a column.
7444      * @param {Number} col The column index
7445      * @param {String} header The new header
7446      */
7447     setColumnHeader : function(col, header){
7448         this.config[col].header = header;
7449         this.fireEvent("headerchange", this, col, header);
7450     },
7451
7452     /**
7453      * Returns the tooltip for the specified column.
7454      * @param {Number} col The column index
7455      * @return {String}
7456      */
7457     getColumnTooltip : function(col){
7458             return this.config[col].tooltip;
7459     },
7460     /**
7461      * Sets the tooltip for a column.
7462      * @param {Number} col The column index
7463      * @param {String} tooltip The new tooltip
7464      */
7465     setColumnTooltip : function(col, tooltip){
7466             this.config[col].tooltip = tooltip;
7467     },
7468
7469     /**
7470      * Returns the dataIndex for the specified column.
7471      * @param {Number} col The column index
7472      * @return {Number}
7473      */
7474     getDataIndex : function(col){
7475         return this.config[col].dataIndex;
7476     },
7477
7478     /**
7479      * Sets the dataIndex for a column.
7480      * @param {Number} col The column index
7481      * @param {Number} dataIndex The new dataIndex
7482      */
7483     setDataIndex : function(col, dataIndex){
7484         this.config[col].dataIndex = dataIndex;
7485     },
7486
7487     
7488     
7489     /**
7490      * Returns true if the cell is editable.
7491      * @param {Number} colIndex The column index
7492      * @param {Number} rowIndex The row index - this is nto actually used..?
7493      * @return {Boolean}
7494      */
7495     isCellEditable : function(colIndex, rowIndex){
7496         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7497     },
7498
7499     /**
7500      * Returns the editor defined for the cell/column.
7501      * return false or null to disable editing.
7502      * @param {Number} colIndex The column index
7503      * @param {Number} rowIndex The row index
7504      * @return {Object}
7505      */
7506     getCellEditor : function(colIndex, rowIndex){
7507         return this.config[colIndex].editor;
7508     },
7509
7510     /**
7511      * Sets if a column is editable.
7512      * @param {Number} col The column index
7513      * @param {Boolean} editable True if the column is editable
7514      */
7515     setEditable : function(col, editable){
7516         this.config[col].editable = editable;
7517     },
7518
7519
7520     /**
7521      * Returns true if the column is hidden.
7522      * @param {Number} colIndex The column index
7523      * @return {Boolean}
7524      */
7525     isHidden : function(colIndex){
7526         return this.config[colIndex].hidden;
7527     },
7528
7529
7530     /**
7531      * Returns true if the column width cannot be changed
7532      */
7533     isFixed : function(colIndex){
7534         return this.config[colIndex].fixed;
7535     },
7536
7537     /**
7538      * Returns true if the column can be resized
7539      * @return {Boolean}
7540      */
7541     isResizable : function(colIndex){
7542         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7543     },
7544     /**
7545      * Sets if a column is hidden.
7546      * @param {Number} colIndex The column index
7547      * @param {Boolean} hidden True if the column is hidden
7548      */
7549     setHidden : function(colIndex, hidden){
7550         this.config[colIndex].hidden = hidden;
7551         this.totalWidth = null;
7552         this.fireEvent("hiddenchange", this, colIndex, hidden);
7553     },
7554
7555     /**
7556      * Sets the editor for a column.
7557      * @param {Number} col The column index
7558      * @param {Object} editor The editor object
7559      */
7560     setEditor : function(col, editor){
7561         this.config[col].editor = editor;
7562     }
7563 });
7564
7565 Roo.grid.ColumnModel.defaultRenderer = function(value)
7566 {
7567     if(typeof value == "object") {
7568         return value;
7569     }
7570         if(typeof value == "string" && value.length < 1){
7571             return "&#160;";
7572         }
7573     
7574         return String.format("{0}", value);
7575 };
7576
7577 // Alias for backwards compatibility
7578 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7579 /*
7580  * Based on:
7581  * Ext JS Library 1.1.1
7582  * Copyright(c) 2006-2007, Ext JS, LLC.
7583  *
7584  * Originally Released Under LGPL - original licence link has changed is not relivant.
7585  *
7586  * Fork - LGPL
7587  * <script type="text/javascript">
7588  */
7589  
7590 /**
7591  * @class Roo.LoadMask
7592  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7593  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7594  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7595  * element's UpdateManager load indicator and will be destroyed after the initial load.
7596  * @constructor
7597  * Create a new LoadMask
7598  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7599  * @param {Object} config The config object
7600  */
7601 Roo.LoadMask = function(el, config){
7602     this.el = Roo.get(el);
7603     Roo.apply(this, config);
7604     if(this.store){
7605         this.store.on('beforeload', this.onBeforeLoad, this);
7606         this.store.on('load', this.onLoad, this);
7607         this.store.on('loadexception', this.onLoadException, this);
7608         this.removeMask = false;
7609     }else{
7610         var um = this.el.getUpdateManager();
7611         um.showLoadIndicator = false; // disable the default indicator
7612         um.on('beforeupdate', this.onBeforeLoad, this);
7613         um.on('update', this.onLoad, this);
7614         um.on('failure', this.onLoad, this);
7615         this.removeMask = true;
7616     }
7617 };
7618
7619 Roo.LoadMask.prototype = {
7620     /**
7621      * @cfg {Boolean} removeMask
7622      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7623      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7624      */
7625     /**
7626      * @cfg {String} msg
7627      * The text to display in a centered loading message box (defaults to 'Loading...')
7628      */
7629     msg : 'Loading...',
7630     /**
7631      * @cfg {String} msgCls
7632      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7633      */
7634     msgCls : 'x-mask-loading',
7635
7636     /**
7637      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7638      * @type Boolean
7639      */
7640     disabled: false,
7641
7642     /**
7643      * Disables the mask to prevent it from being displayed
7644      */
7645     disable : function(){
7646        this.disabled = true;
7647     },
7648
7649     /**
7650      * Enables the mask so that it can be displayed
7651      */
7652     enable : function(){
7653         this.disabled = false;
7654     },
7655     
7656     onLoadException : function()
7657     {
7658         Roo.log(arguments);
7659         
7660         if (typeof(arguments[3]) != 'undefined') {
7661             Roo.MessageBox.alert("Error loading",arguments[3]);
7662         } 
7663         /*
7664         try {
7665             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7666                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7667             }   
7668         } catch(e) {
7669             
7670         }
7671         */
7672     
7673         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7674     },
7675     // private
7676     onLoad : function()
7677     {
7678         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7679     },
7680
7681     // private
7682     onBeforeLoad : function(){
7683         if(!this.disabled){
7684             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7685         }
7686     },
7687
7688     // private
7689     destroy : function(){
7690         if(this.store){
7691             this.store.un('beforeload', this.onBeforeLoad, this);
7692             this.store.un('load', this.onLoad, this);
7693             this.store.un('loadexception', this.onLoadException, this);
7694         }else{
7695             var um = this.el.getUpdateManager();
7696             um.un('beforeupdate', this.onBeforeLoad, this);
7697             um.un('update', this.onLoad, this);
7698             um.un('failure', this.onLoad, this);
7699         }
7700     }
7701 };/*
7702  * - LGPL
7703  *
7704  * table
7705  * 
7706  */
7707
7708 /**
7709  * @class Roo.bootstrap.Table
7710  * @extends Roo.bootstrap.Component
7711  * Bootstrap Table class
7712  * @cfg {String} cls table class
7713  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7714  * @cfg {String} bgcolor Specifies the background color for a table
7715  * @cfg {Number} border Specifies whether the table cells should have borders or not
7716  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7717  * @cfg {Number} cellspacing Specifies the space between cells
7718  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7719  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7720  * @cfg {String} sortable Specifies that the table should be sortable
7721  * @cfg {String} summary Specifies a summary of the content of a table
7722  * @cfg {Number} width Specifies the width of a table
7723  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7724  * 
7725  * @cfg {boolean} striped Should the rows be alternative striped
7726  * @cfg {boolean} bordered Add borders to the table
7727  * @cfg {boolean} hover Add hover highlighting
7728  * @cfg {boolean} condensed Format condensed
7729  * @cfg {boolean} responsive Format condensed
7730  * @cfg {Boolean} loadMask (true|false) default false
7731  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7732  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7733  * @cfg {Boolean} rowSelection (true|false) default false
7734  * @cfg {Boolean} cellSelection (true|false) default false
7735  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7736  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7737  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7738  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7739  
7740  * 
7741  * @constructor
7742  * Create a new Table
7743  * @param {Object} config The config object
7744  */
7745
7746 Roo.bootstrap.Table = function(config){
7747     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7748     
7749   
7750     
7751     // BC...
7752     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7753     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7754     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7755     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7756     
7757     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7758     if (this.sm) {
7759         this.sm.grid = this;
7760         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7761         this.sm = this.selModel;
7762         this.sm.xmodule = this.xmodule || false;
7763     }
7764     
7765     if (this.cm && typeof(this.cm.config) == 'undefined') {
7766         this.colModel = new Roo.grid.ColumnModel(this.cm);
7767         this.cm = this.colModel;
7768         this.cm.xmodule = this.xmodule || false;
7769     }
7770     if (this.store) {
7771         this.store= Roo.factory(this.store, Roo.data);
7772         this.ds = this.store;
7773         this.ds.xmodule = this.xmodule || false;
7774          
7775     }
7776     if (this.footer && this.store) {
7777         this.footer.dataSource = this.ds;
7778         this.footer = Roo.factory(this.footer);
7779     }
7780     
7781     /** @private */
7782     this.addEvents({
7783         /**
7784          * @event cellclick
7785          * Fires when a cell is clicked
7786          * @param {Roo.bootstrap.Table} this
7787          * @param {Roo.Element} el
7788          * @param {Number} rowIndex
7789          * @param {Number} columnIndex
7790          * @param {Roo.EventObject} e
7791          */
7792         "cellclick" : true,
7793         /**
7794          * @event celldblclick
7795          * Fires when a cell is double clicked
7796          * @param {Roo.bootstrap.Table} this
7797          * @param {Roo.Element} el
7798          * @param {Number} rowIndex
7799          * @param {Number} columnIndex
7800          * @param {Roo.EventObject} e
7801          */
7802         "celldblclick" : true,
7803         /**
7804          * @event rowclick
7805          * Fires when a row is clicked
7806          * @param {Roo.bootstrap.Table} this
7807          * @param {Roo.Element} el
7808          * @param {Number} rowIndex
7809          * @param {Roo.EventObject} e
7810          */
7811         "rowclick" : true,
7812         /**
7813          * @event rowdblclick
7814          * Fires when a row is double clicked
7815          * @param {Roo.bootstrap.Table} this
7816          * @param {Roo.Element} el
7817          * @param {Number} rowIndex
7818          * @param {Roo.EventObject} e
7819          */
7820         "rowdblclick" : true,
7821         /**
7822          * @event mouseover
7823          * Fires when a mouseover occur
7824          * @param {Roo.bootstrap.Table} this
7825          * @param {Roo.Element} el
7826          * @param {Number} rowIndex
7827          * @param {Number} columnIndex
7828          * @param {Roo.EventObject} e
7829          */
7830         "mouseover" : true,
7831         /**
7832          * @event mouseout
7833          * Fires when a mouseout occur
7834          * @param {Roo.bootstrap.Table} this
7835          * @param {Roo.Element} el
7836          * @param {Number} rowIndex
7837          * @param {Number} columnIndex
7838          * @param {Roo.EventObject} e
7839          */
7840         "mouseout" : true,
7841         /**
7842          * @event rowclass
7843          * Fires when a row is rendered, so you can change add a style to it.
7844          * @param {Roo.bootstrap.Table} this
7845          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7846          */
7847         'rowclass' : true,
7848           /**
7849          * @event rowsrendered
7850          * Fires when all the  rows have been rendered
7851          * @param {Roo.bootstrap.Table} this
7852          */
7853         'rowsrendered' : true,
7854         /**
7855          * @event contextmenu
7856          * The raw contextmenu event for the entire grid.
7857          * @param {Roo.EventObject} e
7858          */
7859         "contextmenu" : true,
7860         /**
7861          * @event rowcontextmenu
7862          * Fires when a row is right clicked
7863          * @param {Roo.bootstrap.Table} this
7864          * @param {Number} rowIndex
7865          * @param {Roo.EventObject} e
7866          */
7867         "rowcontextmenu" : true,
7868         /**
7869          * @event cellcontextmenu
7870          * Fires when a cell is right clicked
7871          * @param {Roo.bootstrap.Table} this
7872          * @param {Number} rowIndex
7873          * @param {Number} cellIndex
7874          * @param {Roo.EventObject} e
7875          */
7876          "cellcontextmenu" : true,
7877          /**
7878          * @event headercontextmenu
7879          * Fires when a header is right clicked
7880          * @param {Roo.bootstrap.Table} this
7881          * @param {Number} columnIndex
7882          * @param {Roo.EventObject} e
7883          */
7884         "headercontextmenu" : true
7885     });
7886 };
7887
7888 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7889     
7890     cls: false,
7891     align: false,
7892     bgcolor: false,
7893     border: false,
7894     cellpadding: false,
7895     cellspacing: false,
7896     frame: false,
7897     rules: false,
7898     sortable: false,
7899     summary: false,
7900     width: false,
7901     striped : false,
7902     scrollBody : false,
7903     bordered: false,
7904     hover:  false,
7905     condensed : false,
7906     responsive : false,
7907     sm : false,
7908     cm : false,
7909     store : false,
7910     loadMask : false,
7911     footerShow : true,
7912     headerShow : true,
7913   
7914     rowSelection : false,
7915     cellSelection : false,
7916     layout : false,
7917     
7918     // Roo.Element - the tbody
7919     mainBody: false,
7920     // Roo.Element - thead element
7921     mainHead: false,
7922     
7923     container: false, // used by gridpanel...
7924     
7925     lazyLoad : false,
7926     
7927     CSS : Roo.util.CSS,
7928     
7929     auto_hide_footer : false,
7930     
7931     getAutoCreate : function()
7932     {
7933         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7934         
7935         cfg = {
7936             tag: 'table',
7937             cls : 'table',
7938             cn : []
7939         };
7940         if (this.scrollBody) {
7941             cfg.cls += ' table-body-fixed';
7942         }    
7943         if (this.striped) {
7944             cfg.cls += ' table-striped';
7945         }
7946         
7947         if (this.hover) {
7948             cfg.cls += ' table-hover';
7949         }
7950         if (this.bordered) {
7951             cfg.cls += ' table-bordered';
7952         }
7953         if (this.condensed) {
7954             cfg.cls += ' table-condensed';
7955         }
7956         if (this.responsive) {
7957             cfg.cls += ' table-responsive';
7958         }
7959         
7960         if (this.cls) {
7961             cfg.cls+=  ' ' +this.cls;
7962         }
7963         
7964         // this lot should be simplifed...
7965         var _t = this;
7966         var cp = [
7967             'align',
7968             'bgcolor',
7969             'border',
7970             'cellpadding',
7971             'cellspacing',
7972             'frame',
7973             'rules',
7974             'sortable',
7975             'summary',
7976             'width'
7977         ].forEach(function(k) {
7978             if (_t[k]) {
7979                 cfg[k] = _t[k];
7980             }
7981         });
7982         
7983         
7984         if (this.layout) {
7985             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7986         }
7987         
7988         if(this.store || this.cm){
7989             if(this.headerShow){
7990                 cfg.cn.push(this.renderHeader());
7991             }
7992             
7993             cfg.cn.push(this.renderBody());
7994             
7995             if(this.footerShow){
7996                 cfg.cn.push(this.renderFooter());
7997             }
7998             // where does this come from?
7999             //cfg.cls+=  ' TableGrid';
8000         }
8001         
8002         return { cn : [ cfg ] };
8003     },
8004     
8005     initEvents : function()
8006     {   
8007         if(!this.store || !this.cm){
8008             return;
8009         }
8010         if (this.selModel) {
8011             this.selModel.initEvents();
8012         }
8013         
8014         
8015         //Roo.log('initEvents with ds!!!!');
8016         
8017         this.mainBody = this.el.select('tbody', true).first();
8018         this.mainHead = this.el.select('thead', true).first();
8019         this.mainFoot = this.el.select('tfoot', true).first();
8020         
8021         
8022         
8023         var _this = this;
8024         
8025         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8026             e.on('click', _this.sort, _this);
8027         });
8028         
8029         this.mainBody.on("click", this.onClick, this);
8030         this.mainBody.on("dblclick", this.onDblClick, this);
8031         
8032         // why is this done????? = it breaks dialogs??
8033         //this.parent().el.setStyle('position', 'relative');
8034         
8035         
8036         if (this.footer) {
8037             this.footer.parentId = this.id;
8038             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8039             
8040             if(this.lazyLoad){
8041                 this.el.select('tfoot tr td').first().addClass('hide');
8042             }
8043         } 
8044         
8045         if(this.loadMask) {
8046             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8047         }
8048         
8049         this.store.on('load', this.onLoad, this);
8050         this.store.on('beforeload', this.onBeforeLoad, this);
8051         this.store.on('update', this.onUpdate, this);
8052         this.store.on('add', this.onAdd, this);
8053         this.store.on("clear", this.clear, this);
8054         
8055         this.el.on("contextmenu", this.onContextMenu, this);
8056         
8057         this.mainBody.on('scroll', this.onBodyScroll, this);
8058         
8059         this.cm.on("headerchange", this.onHeaderChange, this);
8060         
8061         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8062         
8063     },
8064     
8065     onContextMenu : function(e, t)
8066     {
8067         this.processEvent("contextmenu", e);
8068     },
8069     
8070     processEvent : function(name, e)
8071     {
8072         if (name != 'touchstart' ) {
8073             this.fireEvent(name, e);    
8074         }
8075         
8076         var t = e.getTarget();
8077         
8078         var cell = Roo.get(t);
8079         
8080         if(!cell){
8081             return;
8082         }
8083         
8084         if(cell.findParent('tfoot', false, true)){
8085             return;
8086         }
8087         
8088         if(cell.findParent('thead', false, true)){
8089             
8090             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8091                 cell = Roo.get(t).findParent('th', false, true);
8092                 if (!cell) {
8093                     Roo.log("failed to find th in thead?");
8094                     Roo.log(e.getTarget());
8095                     return;
8096                 }
8097             }
8098             
8099             var cellIndex = cell.dom.cellIndex;
8100             
8101             var ename = name == 'touchstart' ? 'click' : name;
8102             this.fireEvent("header" + ename, this, cellIndex, e);
8103             
8104             return;
8105         }
8106         
8107         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8108             cell = Roo.get(t).findParent('td', false, true);
8109             if (!cell) {
8110                 Roo.log("failed to find th in tbody?");
8111                 Roo.log(e.getTarget());
8112                 return;
8113             }
8114         }
8115         
8116         var row = cell.findParent('tr', false, true);
8117         var cellIndex = cell.dom.cellIndex;
8118         var rowIndex = row.dom.rowIndex - 1;
8119         
8120         if(row !== false){
8121             
8122             this.fireEvent("row" + name, this, rowIndex, e);
8123             
8124             if(cell !== false){
8125             
8126                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8127             }
8128         }
8129         
8130     },
8131     
8132     onMouseover : function(e, el)
8133     {
8134         var cell = Roo.get(el);
8135         
8136         if(!cell){
8137             return;
8138         }
8139         
8140         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8141             cell = cell.findParent('td', false, true);
8142         }
8143         
8144         var row = cell.findParent('tr', false, true);
8145         var cellIndex = cell.dom.cellIndex;
8146         var rowIndex = row.dom.rowIndex - 1; // start from 0
8147         
8148         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8149         
8150     },
8151     
8152     onMouseout : function(e, el)
8153     {
8154         var cell = Roo.get(el);
8155         
8156         if(!cell){
8157             return;
8158         }
8159         
8160         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8161             cell = cell.findParent('td', false, true);
8162         }
8163         
8164         var row = cell.findParent('tr', false, true);
8165         var cellIndex = cell.dom.cellIndex;
8166         var rowIndex = row.dom.rowIndex - 1; // start from 0
8167         
8168         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8169         
8170     },
8171     
8172     onClick : function(e, el)
8173     {
8174         var cell = Roo.get(el);
8175         
8176         if(!cell || (!this.cellSelection && !this.rowSelection)){
8177             return;
8178         }
8179         
8180         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8181             cell = cell.findParent('td', false, true);
8182         }
8183         
8184         if(!cell || typeof(cell) == 'undefined'){
8185             return;
8186         }
8187         
8188         var row = cell.findParent('tr', false, true);
8189         
8190         if(!row || typeof(row) == 'undefined'){
8191             return;
8192         }
8193         
8194         var cellIndex = cell.dom.cellIndex;
8195         var rowIndex = this.getRowIndex(row);
8196         
8197         // why??? - should these not be based on SelectionModel?
8198         if(this.cellSelection){
8199             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8200         }
8201         
8202         if(this.rowSelection){
8203             this.fireEvent('rowclick', this, row, rowIndex, e);
8204         }
8205         
8206         
8207     },
8208         
8209     onDblClick : function(e,el)
8210     {
8211         var cell = Roo.get(el);
8212         
8213         if(!cell || (!this.cellSelection && !this.rowSelection)){
8214             return;
8215         }
8216         
8217         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8218             cell = cell.findParent('td', false, true);
8219         }
8220         
8221         if(!cell || typeof(cell) == 'undefined'){
8222             return;
8223         }
8224         
8225         var row = cell.findParent('tr', false, true);
8226         
8227         if(!row || typeof(row) == 'undefined'){
8228             return;
8229         }
8230         
8231         var cellIndex = cell.dom.cellIndex;
8232         var rowIndex = this.getRowIndex(row);
8233         
8234         if(this.cellSelection){
8235             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8236         }
8237         
8238         if(this.rowSelection){
8239             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8240         }
8241     },
8242     
8243     sort : function(e,el)
8244     {
8245         var col = Roo.get(el);
8246         
8247         if(!col.hasClass('sortable')){
8248             return;
8249         }
8250         
8251         var sort = col.attr('sort');
8252         var dir = 'ASC';
8253         
8254         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8255             dir = 'DESC';
8256         }
8257         
8258         this.store.sortInfo = {field : sort, direction : dir};
8259         
8260         if (this.footer) {
8261             Roo.log("calling footer first");
8262             this.footer.onClick('first');
8263         } else {
8264         
8265             this.store.load({ params : { start : 0 } });
8266         }
8267     },
8268     
8269     renderHeader : function()
8270     {
8271         var header = {
8272             tag: 'thead',
8273             cn : []
8274         };
8275         
8276         var cm = this.cm;
8277         this.totalWidth = 0;
8278         
8279         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8280             
8281             var config = cm.config[i];
8282             
8283             var c = {
8284                 tag: 'th',
8285                 cls : 'x-hcol-' + i,
8286                 style : '',
8287                 html: cm.getColumnHeader(i)
8288             };
8289             
8290             var hh = '';
8291             
8292             if(typeof(config.sortable) != 'undefined' && config.sortable){
8293                 c.cls = 'sortable';
8294                 c.html = '<i class="glyphicon"></i>' + c.html;
8295             }
8296             
8297             // could use BS4 hidden-..-down 
8298             
8299             if(typeof(config.lgHeader) != 'undefined'){
8300                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8301             }
8302             
8303             if(typeof(config.mdHeader) != 'undefined'){
8304                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8305             }
8306             
8307             if(typeof(config.smHeader) != 'undefined'){
8308                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8309             }
8310             
8311             if(typeof(config.xsHeader) != 'undefined'){
8312                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8313             }
8314             
8315             if(hh.length){
8316                 c.html = hh;
8317             }
8318             
8319             if(typeof(config.tooltip) != 'undefined'){
8320                 c.tooltip = config.tooltip;
8321             }
8322             
8323             if(typeof(config.colspan) != 'undefined'){
8324                 c.colspan = config.colspan;
8325             }
8326             
8327             if(typeof(config.hidden) != 'undefined' && config.hidden){
8328                 c.style += ' display:none;';
8329             }
8330             
8331             if(typeof(config.dataIndex) != 'undefined'){
8332                 c.sort = config.dataIndex;
8333             }
8334             
8335            
8336             
8337             if(typeof(config.align) != 'undefined' && config.align.length){
8338                 c.style += ' text-align:' + config.align + ';';
8339             }
8340             
8341             if(typeof(config.width) != 'undefined'){
8342                 c.style += ' width:' + config.width + 'px;';
8343                 this.totalWidth += config.width;
8344             } else {
8345                 this.totalWidth += 100; // assume minimum of 100 per column?
8346             }
8347             
8348             if(typeof(config.cls) != 'undefined'){
8349                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8350             }
8351             
8352             ['xs','sm','md','lg'].map(function(size){
8353                 
8354                 if(typeof(config[size]) == 'undefined'){
8355                     return;
8356                 }
8357                  
8358                 if (!config[size]) { // 0 = hidden
8359                     // BS 4 '0' is treated as hide that column and below.
8360                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8361                     return;
8362                 }
8363                 
8364                 c.cls += ' col-' + size + '-' + config[size] + (
8365                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8366                 );
8367                 
8368                 
8369             });
8370             
8371             header.cn.push(c)
8372         }
8373         
8374         return header;
8375     },
8376     
8377     renderBody : function()
8378     {
8379         var body = {
8380             tag: 'tbody',
8381             cn : [
8382                 {
8383                     tag: 'tr',
8384                     cn : [
8385                         {
8386                             tag : 'td',
8387                             colspan :  this.cm.getColumnCount()
8388                         }
8389                     ]
8390                 }
8391             ]
8392         };
8393         
8394         return body;
8395     },
8396     
8397     renderFooter : function()
8398     {
8399         var footer = {
8400             tag: 'tfoot',
8401             cn : [
8402                 {
8403                     tag: 'tr',
8404                     cn : [
8405                         {
8406                             tag : 'td',
8407                             colspan :  this.cm.getColumnCount()
8408                         }
8409                     ]
8410                 }
8411             ]
8412         };
8413         
8414         return footer;
8415     },
8416     
8417     
8418     
8419     onLoad : function()
8420     {
8421 //        Roo.log('ds onload');
8422         this.clear();
8423         
8424         var _this = this;
8425         var cm = this.cm;
8426         var ds = this.store;
8427         
8428         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8429             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8430             if (_this.store.sortInfo) {
8431                     
8432                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8433                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8434                 }
8435                 
8436                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8437                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8438                 }
8439             }
8440         });
8441         
8442         var tbody =  this.mainBody;
8443               
8444         if(ds.getCount() > 0){
8445             ds.data.each(function(d,rowIndex){
8446                 var row =  this.renderRow(cm, ds, rowIndex);
8447                 
8448                 tbody.createChild(row);
8449                 
8450                 var _this = this;
8451                 
8452                 if(row.cellObjects.length){
8453                     Roo.each(row.cellObjects, function(r){
8454                         _this.renderCellObject(r);
8455                     })
8456                 }
8457                 
8458             }, this);
8459         }
8460         
8461         var tfoot = this.el.select('tfoot', true).first();
8462         
8463         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8464             
8465             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8466             
8467             var total = this.ds.getTotalCount();
8468             
8469             if(this.footer.pageSize < total){
8470                 this.mainFoot.show();
8471             }
8472         }
8473         
8474         Roo.each(this.el.select('tbody td', true).elements, function(e){
8475             e.on('mouseover', _this.onMouseover, _this);
8476         });
8477         
8478         Roo.each(this.el.select('tbody td', true).elements, function(e){
8479             e.on('mouseout', _this.onMouseout, _this);
8480         });
8481         this.fireEvent('rowsrendered', this);
8482         
8483         this.autoSize();
8484     },
8485     
8486     
8487     onUpdate : function(ds,record)
8488     {
8489         this.refreshRow(record);
8490         this.autoSize();
8491     },
8492     
8493     onRemove : function(ds, record, index, isUpdate){
8494         if(isUpdate !== true){
8495             this.fireEvent("beforerowremoved", this, index, record);
8496         }
8497         var bt = this.mainBody.dom;
8498         
8499         var rows = this.el.select('tbody > tr', true).elements;
8500         
8501         if(typeof(rows[index]) != 'undefined'){
8502             bt.removeChild(rows[index].dom);
8503         }
8504         
8505 //        if(bt.rows[index]){
8506 //            bt.removeChild(bt.rows[index]);
8507 //        }
8508         
8509         if(isUpdate !== true){
8510             //this.stripeRows(index);
8511             //this.syncRowHeights(index, index);
8512             //this.layout();
8513             this.fireEvent("rowremoved", this, index, record);
8514         }
8515     },
8516     
8517     onAdd : function(ds, records, rowIndex)
8518     {
8519         //Roo.log('on Add called');
8520         // - note this does not handle multiple adding very well..
8521         var bt = this.mainBody.dom;
8522         for (var i =0 ; i < records.length;i++) {
8523             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8524             //Roo.log(records[i]);
8525             //Roo.log(this.store.getAt(rowIndex+i));
8526             this.insertRow(this.store, rowIndex + i, false);
8527             return;
8528         }
8529         
8530     },
8531     
8532     
8533     refreshRow : function(record){
8534         var ds = this.store, index;
8535         if(typeof record == 'number'){
8536             index = record;
8537             record = ds.getAt(index);
8538         }else{
8539             index = ds.indexOf(record);
8540             if (index < 0) {
8541                 return; // should not happen - but seems to 
8542             }
8543         }
8544         this.insertRow(ds, index, true);
8545         this.autoSize();
8546         this.onRemove(ds, record, index+1, true);
8547         this.autoSize();
8548         //this.syncRowHeights(index, index);
8549         //this.layout();
8550         this.fireEvent("rowupdated", this, index, record);
8551     },
8552     
8553     insertRow : function(dm, rowIndex, isUpdate){
8554         
8555         if(!isUpdate){
8556             this.fireEvent("beforerowsinserted", this, rowIndex);
8557         }
8558             //var s = this.getScrollState();
8559         var row = this.renderRow(this.cm, this.store, rowIndex);
8560         // insert before rowIndex..
8561         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8562         
8563         var _this = this;
8564                 
8565         if(row.cellObjects.length){
8566             Roo.each(row.cellObjects, function(r){
8567                 _this.renderCellObject(r);
8568             })
8569         }
8570             
8571         if(!isUpdate){
8572             this.fireEvent("rowsinserted", this, rowIndex);
8573             //this.syncRowHeights(firstRow, lastRow);
8574             //this.stripeRows(firstRow);
8575             //this.layout();
8576         }
8577         
8578     },
8579     
8580     
8581     getRowDom : function(rowIndex)
8582     {
8583         var rows = this.el.select('tbody > tr', true).elements;
8584         
8585         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8586         
8587     },
8588     // returns the object tree for a tr..
8589   
8590     
8591     renderRow : function(cm, ds, rowIndex) 
8592     {
8593         var d = ds.getAt(rowIndex);
8594         
8595         var row = {
8596             tag : 'tr',
8597             cls : 'x-row-' + rowIndex,
8598             cn : []
8599         };
8600             
8601         var cellObjects = [];
8602         
8603         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8604             var config = cm.config[i];
8605             
8606             var renderer = cm.getRenderer(i);
8607             var value = '';
8608             var id = false;
8609             
8610             if(typeof(renderer) !== 'undefined'){
8611                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8612             }
8613             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8614             // and are rendered into the cells after the row is rendered - using the id for the element.
8615             
8616             if(typeof(value) === 'object'){
8617                 id = Roo.id();
8618                 cellObjects.push({
8619                     container : id,
8620                     cfg : value 
8621                 })
8622             }
8623             
8624             var rowcfg = {
8625                 record: d,
8626                 rowIndex : rowIndex,
8627                 colIndex : i,
8628                 rowClass : ''
8629             };
8630
8631             this.fireEvent('rowclass', this, rowcfg);
8632             
8633             var td = {
8634                 tag: 'td',
8635                 cls : rowcfg.rowClass + ' x-col-' + i,
8636                 style: '',
8637                 html: (typeof(value) === 'object') ? '' : value
8638             };
8639             
8640             if (id) {
8641                 td.id = id;
8642             }
8643             
8644             if(typeof(config.colspan) != 'undefined'){
8645                 td.colspan = config.colspan;
8646             }
8647             
8648             if(typeof(config.hidden) != 'undefined' && config.hidden){
8649                 td.style += ' display:none;';
8650             }
8651             
8652             if(typeof(config.align) != 'undefined' && config.align.length){
8653                 td.style += ' text-align:' + config.align + ';';
8654             }
8655             if(typeof(config.valign) != 'undefined' && config.valign.length){
8656                 td.style += ' vertical-align:' + config.valign + ';';
8657             }
8658             
8659             if(typeof(config.width) != 'undefined'){
8660                 td.style += ' width:' +  config.width + 'px;';
8661             }
8662             
8663             if(typeof(config.cursor) != 'undefined'){
8664                 td.style += ' cursor:' +  config.cursor + ';';
8665             }
8666             
8667             if(typeof(config.cls) != 'undefined'){
8668                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8669             }
8670             
8671             ['xs','sm','md','lg'].map(function(size){
8672                 
8673                 if(typeof(config[size]) == 'undefined'){
8674                     return;
8675                 }
8676                 
8677                 
8678                   
8679                 if (!config[size]) { // 0 = hidden
8680                     // BS 4 '0' is treated as hide that column and below.
8681                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8682                     return;
8683                 }
8684                 
8685                 td.cls += ' col-' + size + '-' + config[size] + (
8686                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8687                 );
8688                  
8689
8690             });
8691             
8692             row.cn.push(td);
8693            
8694         }
8695         
8696         row.cellObjects = cellObjects;
8697         
8698         return row;
8699           
8700     },
8701     
8702     
8703     
8704     onBeforeLoad : function()
8705     {
8706         
8707     },
8708      /**
8709      * Remove all rows
8710      */
8711     clear : function()
8712     {
8713         this.el.select('tbody', true).first().dom.innerHTML = '';
8714     },
8715     /**
8716      * Show or hide a row.
8717      * @param {Number} rowIndex to show or hide
8718      * @param {Boolean} state hide
8719      */
8720     setRowVisibility : function(rowIndex, state)
8721     {
8722         var bt = this.mainBody.dom;
8723         
8724         var rows = this.el.select('tbody > tr', true).elements;
8725         
8726         if(typeof(rows[rowIndex]) == 'undefined'){
8727             return;
8728         }
8729         rows[rowIndex].dom.style.display = state ? '' : 'none';
8730     },
8731     
8732     
8733     getSelectionModel : function(){
8734         if(!this.selModel){
8735             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8736         }
8737         return this.selModel;
8738     },
8739     /*
8740      * Render the Roo.bootstrap object from renderder
8741      */
8742     renderCellObject : function(r)
8743     {
8744         var _this = this;
8745         
8746         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8747         
8748         var t = r.cfg.render(r.container);
8749         
8750         if(r.cfg.cn){
8751             Roo.each(r.cfg.cn, function(c){
8752                 var child = {
8753                     container: t.getChildContainer(),
8754                     cfg: c
8755                 };
8756                 _this.renderCellObject(child);
8757             })
8758         }
8759     },
8760     
8761     getRowIndex : function(row)
8762     {
8763         var rowIndex = -1;
8764         
8765         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8766             if(el != row){
8767                 return;
8768             }
8769             
8770             rowIndex = index;
8771         });
8772         
8773         return rowIndex;
8774     },
8775      /**
8776      * Returns the grid's underlying element = used by panel.Grid
8777      * @return {Element} The element
8778      */
8779     getGridEl : function(){
8780         return this.el;
8781     },
8782      /**
8783      * Forces a resize - used by panel.Grid
8784      * @return {Element} The element
8785      */
8786     autoSize : function()
8787     {
8788         //var ctr = Roo.get(this.container.dom.parentElement);
8789         var ctr = Roo.get(this.el.dom);
8790         
8791         var thd = this.getGridEl().select('thead',true).first();
8792         var tbd = this.getGridEl().select('tbody', true).first();
8793         var tfd = this.getGridEl().select('tfoot', true).first();
8794         
8795         var cw = ctr.getWidth();
8796         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8797         
8798         if (tbd) {
8799             
8800             tbd.setWidth(ctr.getWidth());
8801             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8802             // this needs fixing for various usage - currently only hydra job advers I think..
8803             //tdb.setHeight(
8804             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8805             //); 
8806             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8807             cw -= barsize;
8808         }
8809         cw = Math.max(cw, this.totalWidth);
8810         this.getGridEl().select('tbody tr',true).setWidth(cw);
8811         
8812         // resize 'expandable coloumn?
8813         
8814         return; // we doe not have a view in this design..
8815         
8816     },
8817     onBodyScroll: function()
8818     {
8819         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8820         if(this.mainHead){
8821             this.mainHead.setStyle({
8822                 'position' : 'relative',
8823                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8824             });
8825         }
8826         
8827         if(this.lazyLoad){
8828             
8829             var scrollHeight = this.mainBody.dom.scrollHeight;
8830             
8831             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8832             
8833             var height = this.mainBody.getHeight();
8834             
8835             if(scrollHeight - height == scrollTop) {
8836                 
8837                 var total = this.ds.getTotalCount();
8838                 
8839                 if(this.footer.cursor + this.footer.pageSize < total){
8840                     
8841                     this.footer.ds.load({
8842                         params : {
8843                             start : this.footer.cursor + this.footer.pageSize,
8844                             limit : this.footer.pageSize
8845                         },
8846                         add : true
8847                     });
8848                 }
8849             }
8850             
8851         }
8852     },
8853     
8854     onHeaderChange : function()
8855     {
8856         var header = this.renderHeader();
8857         var table = this.el.select('table', true).first();
8858         
8859         this.mainHead.remove();
8860         this.mainHead = table.createChild(header, this.mainBody, false);
8861     },
8862     
8863     onHiddenChange : function(colModel, colIndex, hidden)
8864     {
8865         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8866         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8867         
8868         this.CSS.updateRule(thSelector, "display", "");
8869         this.CSS.updateRule(tdSelector, "display", "");
8870         
8871         if(hidden){
8872             this.CSS.updateRule(thSelector, "display", "none");
8873             this.CSS.updateRule(tdSelector, "display", "none");
8874         }
8875         
8876         this.onHeaderChange();
8877         this.onLoad();
8878     },
8879     
8880     setColumnWidth: function(col_index, width)
8881     {
8882         // width = "md-2 xs-2..."
8883         if(!this.colModel.config[col_index]) {
8884             return;
8885         }
8886         
8887         var w = width.split(" ");
8888         
8889         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8890         
8891         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8892         
8893         
8894         for(var j = 0; j < w.length; j++) {
8895             
8896             if(!w[j]) {
8897                 continue;
8898             }
8899             
8900             var size_cls = w[j].split("-");
8901             
8902             if(!Number.isInteger(size_cls[1] * 1)) {
8903                 continue;
8904             }
8905             
8906             if(!this.colModel.config[col_index][size_cls[0]]) {
8907                 continue;
8908             }
8909             
8910             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8911                 continue;
8912             }
8913             
8914             h_row[0].classList.replace(
8915                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8916                 "col-"+size_cls[0]+"-"+size_cls[1]
8917             );
8918             
8919             for(var i = 0; i < rows.length; i++) {
8920                 
8921                 var size_cls = w[j].split("-");
8922                 
8923                 if(!Number.isInteger(size_cls[1] * 1)) {
8924                     continue;
8925                 }
8926                 
8927                 if(!this.colModel.config[col_index][size_cls[0]]) {
8928                     continue;
8929                 }
8930                 
8931                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8932                     continue;
8933                 }
8934                 
8935                 rows[i].classList.replace(
8936                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8937                     "col-"+size_cls[0]+"-"+size_cls[1]
8938                 );
8939             }
8940             
8941             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8942         }
8943     }
8944 });
8945
8946  
8947
8948  /*
8949  * - LGPL
8950  *
8951  * table cell
8952  * 
8953  */
8954
8955 /**
8956  * @class Roo.bootstrap.TableCell
8957  * @extends Roo.bootstrap.Component
8958  * Bootstrap TableCell class
8959  * @cfg {String} html cell contain text
8960  * @cfg {String} cls cell class
8961  * @cfg {String} tag cell tag (td|th) default td
8962  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8963  * @cfg {String} align Aligns the content in a cell
8964  * @cfg {String} axis Categorizes cells
8965  * @cfg {String} bgcolor Specifies the background color of a cell
8966  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8967  * @cfg {Number} colspan Specifies the number of columns a cell should span
8968  * @cfg {String} headers Specifies one or more header cells a cell is related to
8969  * @cfg {Number} height Sets the height of a cell
8970  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8971  * @cfg {Number} rowspan Sets the number of rows a cell should span
8972  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8973  * @cfg {String} valign Vertical aligns the content in a cell
8974  * @cfg {Number} width Specifies the width of a cell
8975  * 
8976  * @constructor
8977  * Create a new TableCell
8978  * @param {Object} config The config object
8979  */
8980
8981 Roo.bootstrap.TableCell = function(config){
8982     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8983 };
8984
8985 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8986     
8987     html: false,
8988     cls: false,
8989     tag: false,
8990     abbr: false,
8991     align: false,
8992     axis: false,
8993     bgcolor: false,
8994     charoff: false,
8995     colspan: false,
8996     headers: false,
8997     height: false,
8998     nowrap: false,
8999     rowspan: false,
9000     scope: false,
9001     valign: false,
9002     width: false,
9003     
9004     
9005     getAutoCreate : function(){
9006         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9007         
9008         cfg = {
9009             tag: 'td'
9010         };
9011         
9012         if(this.tag){
9013             cfg.tag = this.tag;
9014         }
9015         
9016         if (this.html) {
9017             cfg.html=this.html
9018         }
9019         if (this.cls) {
9020             cfg.cls=this.cls
9021         }
9022         if (this.abbr) {
9023             cfg.abbr=this.abbr
9024         }
9025         if (this.align) {
9026             cfg.align=this.align
9027         }
9028         if (this.axis) {
9029             cfg.axis=this.axis
9030         }
9031         if (this.bgcolor) {
9032             cfg.bgcolor=this.bgcolor
9033         }
9034         if (this.charoff) {
9035             cfg.charoff=this.charoff
9036         }
9037         if (this.colspan) {
9038             cfg.colspan=this.colspan
9039         }
9040         if (this.headers) {
9041             cfg.headers=this.headers
9042         }
9043         if (this.height) {
9044             cfg.height=this.height
9045         }
9046         if (this.nowrap) {
9047             cfg.nowrap=this.nowrap
9048         }
9049         if (this.rowspan) {
9050             cfg.rowspan=this.rowspan
9051         }
9052         if (this.scope) {
9053             cfg.scope=this.scope
9054         }
9055         if (this.valign) {
9056             cfg.valign=this.valign
9057         }
9058         if (this.width) {
9059             cfg.width=this.width
9060         }
9061         
9062         
9063         return cfg;
9064     }
9065    
9066 });
9067
9068  
9069
9070  /*
9071  * - LGPL
9072  *
9073  * table row
9074  * 
9075  */
9076
9077 /**
9078  * @class Roo.bootstrap.TableRow
9079  * @extends Roo.bootstrap.Component
9080  * Bootstrap TableRow class
9081  * @cfg {String} cls row class
9082  * @cfg {String} align Aligns the content in a table row
9083  * @cfg {String} bgcolor Specifies a background color for a table row
9084  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9085  * @cfg {String} valign Vertical aligns the content in a table row
9086  * 
9087  * @constructor
9088  * Create a new TableRow
9089  * @param {Object} config The config object
9090  */
9091
9092 Roo.bootstrap.TableRow = function(config){
9093     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9094 };
9095
9096 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9097     
9098     cls: false,
9099     align: false,
9100     bgcolor: false,
9101     charoff: false,
9102     valign: false,
9103     
9104     getAutoCreate : function(){
9105         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9106         
9107         cfg = {
9108             tag: 'tr'
9109         };
9110             
9111         if(this.cls){
9112             cfg.cls = this.cls;
9113         }
9114         if(this.align){
9115             cfg.align = this.align;
9116         }
9117         if(this.bgcolor){
9118             cfg.bgcolor = this.bgcolor;
9119         }
9120         if(this.charoff){
9121             cfg.charoff = this.charoff;
9122         }
9123         if(this.valign){
9124             cfg.valign = this.valign;
9125         }
9126         
9127         return cfg;
9128     }
9129    
9130 });
9131
9132  
9133
9134  /*
9135  * - LGPL
9136  *
9137  * table body
9138  * 
9139  */
9140
9141 /**
9142  * @class Roo.bootstrap.TableBody
9143  * @extends Roo.bootstrap.Component
9144  * Bootstrap TableBody class
9145  * @cfg {String} cls element class
9146  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9147  * @cfg {String} align Aligns the content inside the element
9148  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9149  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9150  * 
9151  * @constructor
9152  * Create a new TableBody
9153  * @param {Object} config The config object
9154  */
9155
9156 Roo.bootstrap.TableBody = function(config){
9157     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9158 };
9159
9160 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9161     
9162     cls: false,
9163     tag: false,
9164     align: false,
9165     charoff: false,
9166     valign: false,
9167     
9168     getAutoCreate : function(){
9169         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9170         
9171         cfg = {
9172             tag: 'tbody'
9173         };
9174             
9175         if (this.cls) {
9176             cfg.cls=this.cls
9177         }
9178         if(this.tag){
9179             cfg.tag = this.tag;
9180         }
9181         
9182         if(this.align){
9183             cfg.align = this.align;
9184         }
9185         if(this.charoff){
9186             cfg.charoff = this.charoff;
9187         }
9188         if(this.valign){
9189             cfg.valign = this.valign;
9190         }
9191         
9192         return cfg;
9193     }
9194     
9195     
9196 //    initEvents : function()
9197 //    {
9198 //        
9199 //        if(!this.store){
9200 //            return;
9201 //        }
9202 //        
9203 //        this.store = Roo.factory(this.store, Roo.data);
9204 //        this.store.on('load', this.onLoad, this);
9205 //        
9206 //        this.store.load();
9207 //        
9208 //    },
9209 //    
9210 //    onLoad: function () 
9211 //    {   
9212 //        this.fireEvent('load', this);
9213 //    }
9214 //    
9215 //   
9216 });
9217
9218  
9219
9220  /*
9221  * Based on:
9222  * Ext JS Library 1.1.1
9223  * Copyright(c) 2006-2007, Ext JS, LLC.
9224  *
9225  * Originally Released Under LGPL - original licence link has changed is not relivant.
9226  *
9227  * Fork - LGPL
9228  * <script type="text/javascript">
9229  */
9230
9231 // as we use this in bootstrap.
9232 Roo.namespace('Roo.form');
9233  /**
9234  * @class Roo.form.Action
9235  * Internal Class used to handle form actions
9236  * @constructor
9237  * @param {Roo.form.BasicForm} el The form element or its id
9238  * @param {Object} config Configuration options
9239  */
9240
9241  
9242  
9243 // define the action interface
9244 Roo.form.Action = function(form, options){
9245     this.form = form;
9246     this.options = options || {};
9247 };
9248 /**
9249  * Client Validation Failed
9250  * @const 
9251  */
9252 Roo.form.Action.CLIENT_INVALID = 'client';
9253 /**
9254  * Server Validation Failed
9255  * @const 
9256  */
9257 Roo.form.Action.SERVER_INVALID = 'server';
9258  /**
9259  * Connect to Server Failed
9260  * @const 
9261  */
9262 Roo.form.Action.CONNECT_FAILURE = 'connect';
9263 /**
9264  * Reading Data from Server Failed
9265  * @const 
9266  */
9267 Roo.form.Action.LOAD_FAILURE = 'load';
9268
9269 Roo.form.Action.prototype = {
9270     type : 'default',
9271     failureType : undefined,
9272     response : undefined,
9273     result : undefined,
9274
9275     // interface method
9276     run : function(options){
9277
9278     },
9279
9280     // interface method
9281     success : function(response){
9282
9283     },
9284
9285     // interface method
9286     handleResponse : function(response){
9287
9288     },
9289
9290     // default connection failure
9291     failure : function(response){
9292         
9293         this.response = response;
9294         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9295         this.form.afterAction(this, false);
9296     },
9297
9298     processResponse : function(response){
9299         this.response = response;
9300         if(!response.responseText){
9301             return true;
9302         }
9303         this.result = this.handleResponse(response);
9304         return this.result;
9305     },
9306
9307     // utility functions used internally
9308     getUrl : function(appendParams){
9309         var url = this.options.url || this.form.url || this.form.el.dom.action;
9310         if(appendParams){
9311             var p = this.getParams();
9312             if(p){
9313                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9314             }
9315         }
9316         return url;
9317     },
9318
9319     getMethod : function(){
9320         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9321     },
9322
9323     getParams : function(){
9324         var bp = this.form.baseParams;
9325         var p = this.options.params;
9326         if(p){
9327             if(typeof p == "object"){
9328                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9329             }else if(typeof p == 'string' && bp){
9330                 p += '&' + Roo.urlEncode(bp);
9331             }
9332         }else if(bp){
9333             p = Roo.urlEncode(bp);
9334         }
9335         return p;
9336     },
9337
9338     createCallback : function(){
9339         return {
9340             success: this.success,
9341             failure: this.failure,
9342             scope: this,
9343             timeout: (this.form.timeout*1000),
9344             upload: this.form.fileUpload ? this.success : undefined
9345         };
9346     }
9347 };
9348
9349 Roo.form.Action.Submit = function(form, options){
9350     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9351 };
9352
9353 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9354     type : 'submit',
9355
9356     haveProgress : false,
9357     uploadComplete : false,
9358     
9359     // uploadProgress indicator.
9360     uploadProgress : function()
9361     {
9362         if (!this.form.progressUrl) {
9363             return;
9364         }
9365         
9366         if (!this.haveProgress) {
9367             Roo.MessageBox.progress("Uploading", "Uploading");
9368         }
9369         if (this.uploadComplete) {
9370            Roo.MessageBox.hide();
9371            return;
9372         }
9373         
9374         this.haveProgress = true;
9375    
9376         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9377         
9378         var c = new Roo.data.Connection();
9379         c.request({
9380             url : this.form.progressUrl,
9381             params: {
9382                 id : uid
9383             },
9384             method: 'GET',
9385             success : function(req){
9386                //console.log(data);
9387                 var rdata = false;
9388                 var edata;
9389                 try  {
9390                    rdata = Roo.decode(req.responseText)
9391                 } catch (e) {
9392                     Roo.log("Invalid data from server..");
9393                     Roo.log(edata);
9394                     return;
9395                 }
9396                 if (!rdata || !rdata.success) {
9397                     Roo.log(rdata);
9398                     Roo.MessageBox.alert(Roo.encode(rdata));
9399                     return;
9400                 }
9401                 var data = rdata.data;
9402                 
9403                 if (this.uploadComplete) {
9404                    Roo.MessageBox.hide();
9405                    return;
9406                 }
9407                    
9408                 if (data){
9409                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9410                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9411                     );
9412                 }
9413                 this.uploadProgress.defer(2000,this);
9414             },
9415        
9416             failure: function(data) {
9417                 Roo.log('progress url failed ');
9418                 Roo.log(data);
9419             },
9420             scope : this
9421         });
9422            
9423     },
9424     
9425     
9426     run : function()
9427     {
9428         // run get Values on the form, so it syncs any secondary forms.
9429         this.form.getValues();
9430         
9431         var o = this.options;
9432         var method = this.getMethod();
9433         var isPost = method == 'POST';
9434         if(o.clientValidation === false || this.form.isValid()){
9435             
9436             if (this.form.progressUrl) {
9437                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9438                     (new Date() * 1) + '' + Math.random());
9439                     
9440             } 
9441             
9442             
9443             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9444                 form:this.form.el.dom,
9445                 url:this.getUrl(!isPost),
9446                 method: method,
9447                 params:isPost ? this.getParams() : null,
9448                 isUpload: this.form.fileUpload,
9449                 formData : this.form.formData
9450             }));
9451             
9452             this.uploadProgress();
9453
9454         }else if (o.clientValidation !== false){ // client validation failed
9455             this.failureType = Roo.form.Action.CLIENT_INVALID;
9456             this.form.afterAction(this, false);
9457         }
9458     },
9459
9460     success : function(response)
9461     {
9462         this.uploadComplete= true;
9463         if (this.haveProgress) {
9464             Roo.MessageBox.hide();
9465         }
9466         
9467         
9468         var result = this.processResponse(response);
9469         if(result === true || result.success){
9470             this.form.afterAction(this, true);
9471             return;
9472         }
9473         if(result.errors){
9474             this.form.markInvalid(result.errors);
9475             this.failureType = Roo.form.Action.SERVER_INVALID;
9476         }
9477         this.form.afterAction(this, false);
9478     },
9479     failure : function(response)
9480     {
9481         this.uploadComplete= true;
9482         if (this.haveProgress) {
9483             Roo.MessageBox.hide();
9484         }
9485         
9486         this.response = response;
9487         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9488         this.form.afterAction(this, false);
9489     },
9490     
9491     handleResponse : function(response){
9492         if(this.form.errorReader){
9493             var rs = this.form.errorReader.read(response);
9494             var errors = [];
9495             if(rs.records){
9496                 for(var i = 0, len = rs.records.length; i < len; i++) {
9497                     var r = rs.records[i];
9498                     errors[i] = r.data;
9499                 }
9500             }
9501             if(errors.length < 1){
9502                 errors = null;
9503             }
9504             return {
9505                 success : rs.success,
9506                 errors : errors
9507             };
9508         }
9509         var ret = false;
9510         try {
9511             ret = Roo.decode(response.responseText);
9512         } catch (e) {
9513             ret = {
9514                 success: false,
9515                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9516                 errors : []
9517             };
9518         }
9519         return ret;
9520         
9521     }
9522 });
9523
9524
9525 Roo.form.Action.Load = function(form, options){
9526     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9527     this.reader = this.form.reader;
9528 };
9529
9530 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9531     type : 'load',
9532
9533     run : function(){
9534         
9535         Roo.Ajax.request(Roo.apply(
9536                 this.createCallback(), {
9537                     method:this.getMethod(),
9538                     url:this.getUrl(false),
9539                     params:this.getParams()
9540         }));
9541     },
9542
9543     success : function(response){
9544         
9545         var result = this.processResponse(response);
9546         if(result === true || !result.success || !result.data){
9547             this.failureType = Roo.form.Action.LOAD_FAILURE;
9548             this.form.afterAction(this, false);
9549             return;
9550         }
9551         this.form.clearInvalid();
9552         this.form.setValues(result.data);
9553         this.form.afterAction(this, true);
9554     },
9555
9556     handleResponse : function(response){
9557         if(this.form.reader){
9558             var rs = this.form.reader.read(response);
9559             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9560             return {
9561                 success : rs.success,
9562                 data : data
9563             };
9564         }
9565         return Roo.decode(response.responseText);
9566     }
9567 });
9568
9569 Roo.form.Action.ACTION_TYPES = {
9570     'load' : Roo.form.Action.Load,
9571     'submit' : Roo.form.Action.Submit
9572 };/*
9573  * - LGPL
9574  *
9575  * form
9576  *
9577  */
9578
9579 /**
9580  * @class Roo.bootstrap.Form
9581  * @extends Roo.bootstrap.Component
9582  * Bootstrap Form class
9583  * @cfg {String} method  GET | POST (default POST)
9584  * @cfg {String} labelAlign top | left (default top)
9585  * @cfg {String} align left  | right - for navbars
9586  * @cfg {Boolean} loadMask load mask when submit (default true)
9587
9588  *
9589  * @constructor
9590  * Create a new Form
9591  * @param {Object} config The config object
9592  */
9593
9594
9595 Roo.bootstrap.Form = function(config){
9596     
9597     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9598     
9599     Roo.bootstrap.Form.popover.apply();
9600     
9601     this.addEvents({
9602         /**
9603          * @event clientvalidation
9604          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9605          * @param {Form} this
9606          * @param {Boolean} valid true if the form has passed client-side validation
9607          */
9608         clientvalidation: true,
9609         /**
9610          * @event beforeaction
9611          * Fires before any action is performed. Return false to cancel the action.
9612          * @param {Form} this
9613          * @param {Action} action The action to be performed
9614          */
9615         beforeaction: true,
9616         /**
9617          * @event actionfailed
9618          * Fires when an action fails.
9619          * @param {Form} this
9620          * @param {Action} action The action that failed
9621          */
9622         actionfailed : true,
9623         /**
9624          * @event actioncomplete
9625          * Fires when an action is completed.
9626          * @param {Form} this
9627          * @param {Action} action The action that completed
9628          */
9629         actioncomplete : true
9630     });
9631 };
9632
9633 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9634
9635      /**
9636      * @cfg {String} method
9637      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9638      */
9639     method : 'POST',
9640     /**
9641      * @cfg {String} url
9642      * The URL to use for form actions if one isn't supplied in the action options.
9643      */
9644     /**
9645      * @cfg {Boolean} fileUpload
9646      * Set to true if this form is a file upload.
9647      */
9648
9649     /**
9650      * @cfg {Object} baseParams
9651      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9652      */
9653
9654     /**
9655      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9656      */
9657     timeout: 30,
9658     /**
9659      * @cfg {Sting} align (left|right) for navbar forms
9660      */
9661     align : 'left',
9662
9663     // private
9664     activeAction : null,
9665
9666     /**
9667      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9668      * element by passing it or its id or mask the form itself by passing in true.
9669      * @type Mixed
9670      */
9671     waitMsgTarget : false,
9672
9673     loadMask : true,
9674     
9675     /**
9676      * @cfg {Boolean} errorMask (true|false) default false
9677      */
9678     errorMask : false,
9679     
9680     /**
9681      * @cfg {Number} maskOffset Default 100
9682      */
9683     maskOffset : 100,
9684     
9685     /**
9686      * @cfg {Boolean} maskBody
9687      */
9688     maskBody : false,
9689
9690     getAutoCreate : function(){
9691
9692         var cfg = {
9693             tag: 'form',
9694             method : this.method || 'POST',
9695             id : this.id || Roo.id(),
9696             cls : ''
9697         };
9698         if (this.parent().xtype.match(/^Nav/)) {
9699             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9700
9701         }
9702
9703         if (this.labelAlign == 'left' ) {
9704             cfg.cls += ' form-horizontal';
9705         }
9706
9707
9708         return cfg;
9709     },
9710     initEvents : function()
9711     {
9712         this.el.on('submit', this.onSubmit, this);
9713         // this was added as random key presses on the form where triggering form submit.
9714         this.el.on('keypress', function(e) {
9715             if (e.getCharCode() != 13) {
9716                 return true;
9717             }
9718             // we might need to allow it for textareas.. and some other items.
9719             // check e.getTarget().
9720
9721             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9722                 return true;
9723             }
9724
9725             Roo.log("keypress blocked");
9726
9727             e.preventDefault();
9728             return false;
9729         });
9730         
9731     },
9732     // private
9733     onSubmit : function(e){
9734         e.stopEvent();
9735     },
9736
9737      /**
9738      * Returns true if client-side validation on the form is successful.
9739      * @return Boolean
9740      */
9741     isValid : function(){
9742         var items = this.getItems();
9743         var valid = true;
9744         var target = false;
9745         
9746         items.each(function(f){
9747             
9748             if(f.validate()){
9749                 return;
9750             }
9751             
9752             Roo.log('invalid field: ' + f.name);
9753             
9754             valid = false;
9755
9756             if(!target && f.el.isVisible(true)){
9757                 target = f;
9758             }
9759            
9760         });
9761         
9762         if(this.errorMask && !valid){
9763             Roo.bootstrap.Form.popover.mask(this, target);
9764         }
9765         
9766         return valid;
9767     },
9768     
9769     /**
9770      * Returns true if any fields in this form have changed since their original load.
9771      * @return Boolean
9772      */
9773     isDirty : function(){
9774         var dirty = false;
9775         var items = this.getItems();
9776         items.each(function(f){
9777            if(f.isDirty()){
9778                dirty = true;
9779                return false;
9780            }
9781            return true;
9782         });
9783         return dirty;
9784     },
9785      /**
9786      * Performs a predefined action (submit or load) or custom actions you define on this form.
9787      * @param {String} actionName The name of the action type
9788      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9789      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9790      * accept other config options):
9791      * <pre>
9792 Property          Type             Description
9793 ----------------  ---------------  ----------------------------------------------------------------------------------
9794 url               String           The url for the action (defaults to the form's url)
9795 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9796 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9797 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9798                                    validate the form on the client (defaults to false)
9799      * </pre>
9800      * @return {BasicForm} this
9801      */
9802     doAction : function(action, options){
9803         if(typeof action == 'string'){
9804             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9805         }
9806         if(this.fireEvent('beforeaction', this, action) !== false){
9807             this.beforeAction(action);
9808             action.run.defer(100, action);
9809         }
9810         return this;
9811     },
9812
9813     // private
9814     beforeAction : function(action){
9815         var o = action.options;
9816         
9817         if(this.loadMask){
9818             
9819             if(this.maskBody){
9820                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9821             } else {
9822                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9823             }
9824         }
9825         // not really supported yet.. ??
9826
9827         //if(this.waitMsgTarget === true){
9828         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9829         //}else if(this.waitMsgTarget){
9830         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9831         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9832         //}else {
9833         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9834        // }
9835
9836     },
9837
9838     // private
9839     afterAction : function(action, success){
9840         this.activeAction = null;
9841         var o = action.options;
9842
9843         if(this.loadMask){
9844             
9845             if(this.maskBody){
9846                 Roo.get(document.body).unmask();
9847             } else {
9848                 this.el.unmask();
9849             }
9850         }
9851         
9852         //if(this.waitMsgTarget === true){
9853 //            this.el.unmask();
9854         //}else if(this.waitMsgTarget){
9855         //    this.waitMsgTarget.unmask();
9856         //}else{
9857         //    Roo.MessageBox.updateProgress(1);
9858         //    Roo.MessageBox.hide();
9859        // }
9860         //
9861         if(success){
9862             if(o.reset){
9863                 this.reset();
9864             }
9865             Roo.callback(o.success, o.scope, [this, action]);
9866             this.fireEvent('actioncomplete', this, action);
9867
9868         }else{
9869
9870             // failure condition..
9871             // we have a scenario where updates need confirming.
9872             // eg. if a locking scenario exists..
9873             // we look for { errors : { needs_confirm : true }} in the response.
9874             if (
9875                 (typeof(action.result) != 'undefined')  &&
9876                 (typeof(action.result.errors) != 'undefined')  &&
9877                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9878            ){
9879                 var _t = this;
9880                 Roo.log("not supported yet");
9881                  /*
9882
9883                 Roo.MessageBox.confirm(
9884                     "Change requires confirmation",
9885                     action.result.errorMsg,
9886                     function(r) {
9887                         if (r != 'yes') {
9888                             return;
9889                         }
9890                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9891                     }
9892
9893                 );
9894                 */
9895
9896
9897                 return;
9898             }
9899
9900             Roo.callback(o.failure, o.scope, [this, action]);
9901             // show an error message if no failed handler is set..
9902             if (!this.hasListener('actionfailed')) {
9903                 Roo.log("need to add dialog support");
9904                 /*
9905                 Roo.MessageBox.alert("Error",
9906                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9907                         action.result.errorMsg :
9908                         "Saving Failed, please check your entries or try again"
9909                 );
9910                 */
9911             }
9912
9913             this.fireEvent('actionfailed', this, action);
9914         }
9915
9916     },
9917     /**
9918      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9919      * @param {String} id The value to search for
9920      * @return Field
9921      */
9922     findField : function(id){
9923         var items = this.getItems();
9924         var field = items.get(id);
9925         if(!field){
9926              items.each(function(f){
9927                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9928                     field = f;
9929                     return false;
9930                 }
9931                 return true;
9932             });
9933         }
9934         return field || null;
9935     },
9936      /**
9937      * Mark fields in this form invalid in bulk.
9938      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9939      * @return {BasicForm} this
9940      */
9941     markInvalid : function(errors){
9942         if(errors instanceof Array){
9943             for(var i = 0, len = errors.length; i < len; i++){
9944                 var fieldError = errors[i];
9945                 var f = this.findField(fieldError.id);
9946                 if(f){
9947                     f.markInvalid(fieldError.msg);
9948                 }
9949             }
9950         }else{
9951             var field, id;
9952             for(id in errors){
9953                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9954                     field.markInvalid(errors[id]);
9955                 }
9956             }
9957         }
9958         //Roo.each(this.childForms || [], function (f) {
9959         //    f.markInvalid(errors);
9960         //});
9961
9962         return this;
9963     },
9964
9965     /**
9966      * Set values for fields in this form in bulk.
9967      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9968      * @return {BasicForm} this
9969      */
9970     setValues : function(values){
9971         if(values instanceof Array){ // array of objects
9972             for(var i = 0, len = values.length; i < len; i++){
9973                 var v = values[i];
9974                 var f = this.findField(v.id);
9975                 if(f){
9976                     f.setValue(v.value);
9977                     if(this.trackResetOnLoad){
9978                         f.originalValue = f.getValue();
9979                     }
9980                 }
9981             }
9982         }else{ // object hash
9983             var field, id;
9984             for(id in values){
9985                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9986
9987                     if (field.setFromData &&
9988                         field.valueField &&
9989                         field.displayField &&
9990                         // combos' with local stores can
9991                         // be queried via setValue()
9992                         // to set their value..
9993                         (field.store && !field.store.isLocal)
9994                         ) {
9995                         // it's a combo
9996                         var sd = { };
9997                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9998                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9999                         field.setFromData(sd);
10000
10001                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10002                         
10003                         field.setFromData(values);
10004                         
10005                     } else {
10006                         field.setValue(values[id]);
10007                     }
10008
10009
10010                     if(this.trackResetOnLoad){
10011                         field.originalValue = field.getValue();
10012                     }
10013                 }
10014             }
10015         }
10016
10017         //Roo.each(this.childForms || [], function (f) {
10018         //    f.setValues(values);
10019         //});
10020
10021         return this;
10022     },
10023
10024     /**
10025      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10026      * they are returned as an array.
10027      * @param {Boolean} asString
10028      * @return {Object}
10029      */
10030     getValues : function(asString){
10031         //if (this.childForms) {
10032             // copy values from the child forms
10033         //    Roo.each(this.childForms, function (f) {
10034         //        this.setValues(f.getValues());
10035         //    }, this);
10036         //}
10037
10038
10039
10040         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10041         if(asString === true){
10042             return fs;
10043         }
10044         return Roo.urlDecode(fs);
10045     },
10046
10047     /**
10048      * Returns the fields in this form as an object with key/value pairs.
10049      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10050      * @return {Object}
10051      */
10052     getFieldValues : function(with_hidden)
10053     {
10054         var items = this.getItems();
10055         var ret = {};
10056         items.each(function(f){
10057             
10058             if (!f.getName()) {
10059                 return;
10060             }
10061             
10062             var v = f.getValue();
10063             
10064             if (f.inputType =='radio') {
10065                 if (typeof(ret[f.getName()]) == 'undefined') {
10066                     ret[f.getName()] = ''; // empty..
10067                 }
10068
10069                 if (!f.el.dom.checked) {
10070                     return;
10071
10072                 }
10073                 v = f.el.dom.value;
10074
10075             }
10076             
10077             if(f.xtype == 'MoneyField'){
10078                 ret[f.currencyName] = f.getCurrency();
10079             }
10080
10081             // not sure if this supported any more..
10082             if ((typeof(v) == 'object') && f.getRawValue) {
10083                 v = f.getRawValue() ; // dates..
10084             }
10085             // combo boxes where name != hiddenName...
10086             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10087                 ret[f.name] = f.getRawValue();
10088             }
10089             ret[f.getName()] = v;
10090         });
10091
10092         return ret;
10093     },
10094
10095     /**
10096      * Clears all invalid messages in this form.
10097      * @return {BasicForm} this
10098      */
10099     clearInvalid : function(){
10100         var items = this.getItems();
10101
10102         items.each(function(f){
10103            f.clearInvalid();
10104         });
10105
10106         return this;
10107     },
10108
10109     /**
10110      * Resets this form.
10111      * @return {BasicForm} this
10112      */
10113     reset : function(){
10114         var items = this.getItems();
10115         items.each(function(f){
10116             f.reset();
10117         });
10118
10119         Roo.each(this.childForms || [], function (f) {
10120             f.reset();
10121         });
10122
10123
10124         return this;
10125     },
10126     
10127     getItems : function()
10128     {
10129         var r=new Roo.util.MixedCollection(false, function(o){
10130             return o.id || (o.id = Roo.id());
10131         });
10132         var iter = function(el) {
10133             if (el.inputEl) {
10134                 r.add(el);
10135             }
10136             if (!el.items) {
10137                 return;
10138             }
10139             Roo.each(el.items,function(e) {
10140                 iter(e);
10141             });
10142         };
10143
10144         iter(this);
10145         return r;
10146     },
10147     
10148     hideFields : function(items)
10149     {
10150         Roo.each(items, function(i){
10151             
10152             var f = this.findField(i);
10153             
10154             if(!f){
10155                 return;
10156             }
10157             
10158             f.hide();
10159             
10160         }, this);
10161     },
10162     
10163     showFields : function(items)
10164     {
10165         Roo.each(items, function(i){
10166             
10167             var f = this.findField(i);
10168             
10169             if(!f){
10170                 return;
10171             }
10172             
10173             f.show();
10174             
10175         }, this);
10176     }
10177
10178 });
10179
10180 Roo.apply(Roo.bootstrap.Form, {
10181     
10182     popover : {
10183         
10184         padding : 5,
10185         
10186         isApplied : false,
10187         
10188         isMasked : false,
10189         
10190         form : false,
10191         
10192         target : false,
10193         
10194         toolTip : false,
10195         
10196         intervalID : false,
10197         
10198         maskEl : false,
10199         
10200         apply : function()
10201         {
10202             if(this.isApplied){
10203                 return;
10204             }
10205             
10206             this.maskEl = {
10207                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10208                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10209                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10210                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10211             };
10212             
10213             this.maskEl.top.enableDisplayMode("block");
10214             this.maskEl.left.enableDisplayMode("block");
10215             this.maskEl.bottom.enableDisplayMode("block");
10216             this.maskEl.right.enableDisplayMode("block");
10217             
10218             this.toolTip = new Roo.bootstrap.Tooltip({
10219                 cls : 'roo-form-error-popover',
10220                 alignment : {
10221                     'left' : ['r-l', [-2,0], 'right'],
10222                     'right' : ['l-r', [2,0], 'left'],
10223                     'bottom' : ['tl-bl', [0,2], 'top'],
10224                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10225                 }
10226             });
10227             
10228             this.toolTip.render(Roo.get(document.body));
10229
10230             this.toolTip.el.enableDisplayMode("block");
10231             
10232             Roo.get(document.body).on('click', function(){
10233                 this.unmask();
10234             }, this);
10235             
10236             Roo.get(document.body).on('touchstart', function(){
10237                 this.unmask();
10238             }, this);
10239             
10240             this.isApplied = true
10241         },
10242         
10243         mask : function(form, target)
10244         {
10245             this.form = form;
10246             
10247             this.target = target;
10248             
10249             if(!this.form.errorMask || !target.el){
10250                 return;
10251             }
10252             
10253             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10254             
10255             Roo.log(scrollable);
10256             
10257             var ot = this.target.el.calcOffsetsTo(scrollable);
10258             
10259             var scrollTo = ot[1] - this.form.maskOffset;
10260             
10261             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10262             
10263             scrollable.scrollTo('top', scrollTo);
10264             
10265             var box = this.target.el.getBox();
10266             Roo.log(box);
10267             var zIndex = Roo.bootstrap.Modal.zIndex++;
10268
10269             
10270             this.maskEl.top.setStyle('position', 'absolute');
10271             this.maskEl.top.setStyle('z-index', zIndex);
10272             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10273             this.maskEl.top.setLeft(0);
10274             this.maskEl.top.setTop(0);
10275             this.maskEl.top.show();
10276             
10277             this.maskEl.left.setStyle('position', 'absolute');
10278             this.maskEl.left.setStyle('z-index', zIndex);
10279             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10280             this.maskEl.left.setLeft(0);
10281             this.maskEl.left.setTop(box.y - this.padding);
10282             this.maskEl.left.show();
10283
10284             this.maskEl.bottom.setStyle('position', 'absolute');
10285             this.maskEl.bottom.setStyle('z-index', zIndex);
10286             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10287             this.maskEl.bottom.setLeft(0);
10288             this.maskEl.bottom.setTop(box.bottom + this.padding);
10289             this.maskEl.bottom.show();
10290
10291             this.maskEl.right.setStyle('position', 'absolute');
10292             this.maskEl.right.setStyle('z-index', zIndex);
10293             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10294             this.maskEl.right.setLeft(box.right + this.padding);
10295             this.maskEl.right.setTop(box.y - this.padding);
10296             this.maskEl.right.show();
10297
10298             this.toolTip.bindEl = this.target.el;
10299
10300             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10301
10302             var tip = this.target.blankText;
10303
10304             if(this.target.getValue() !== '' ) {
10305                 
10306                 if (this.target.invalidText.length) {
10307                     tip = this.target.invalidText;
10308                 } else if (this.target.regexText.length){
10309                     tip = this.target.regexText;
10310                 }
10311             }
10312
10313             this.toolTip.show(tip);
10314
10315             this.intervalID = window.setInterval(function() {
10316                 Roo.bootstrap.Form.popover.unmask();
10317             }, 10000);
10318
10319             window.onwheel = function(){ return false;};
10320             
10321             (function(){ this.isMasked = true; }).defer(500, this);
10322             
10323         },
10324         
10325         unmask : function()
10326         {
10327             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10328                 return;
10329             }
10330             
10331             this.maskEl.top.setStyle('position', 'absolute');
10332             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10333             this.maskEl.top.hide();
10334
10335             this.maskEl.left.setStyle('position', 'absolute');
10336             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10337             this.maskEl.left.hide();
10338
10339             this.maskEl.bottom.setStyle('position', 'absolute');
10340             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10341             this.maskEl.bottom.hide();
10342
10343             this.maskEl.right.setStyle('position', 'absolute');
10344             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10345             this.maskEl.right.hide();
10346             
10347             this.toolTip.hide();
10348             
10349             this.toolTip.el.hide();
10350             
10351             window.onwheel = function(){ return true;};
10352             
10353             if(this.intervalID){
10354                 window.clearInterval(this.intervalID);
10355                 this.intervalID = false;
10356             }
10357             
10358             this.isMasked = false;
10359             
10360         }
10361         
10362     }
10363     
10364 });
10365
10366 /*
10367  * Based on:
10368  * Ext JS Library 1.1.1
10369  * Copyright(c) 2006-2007, Ext JS, LLC.
10370  *
10371  * Originally Released Under LGPL - original licence link has changed is not relivant.
10372  *
10373  * Fork - LGPL
10374  * <script type="text/javascript">
10375  */
10376 /**
10377  * @class Roo.form.VTypes
10378  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10379  * @singleton
10380  */
10381 Roo.form.VTypes = function(){
10382     // closure these in so they are only created once.
10383     var alpha = /^[a-zA-Z_]+$/;
10384     var alphanum = /^[a-zA-Z0-9_]+$/;
10385     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10386     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10387
10388     // All these messages and functions are configurable
10389     return {
10390         /**
10391          * The function used to validate email addresses
10392          * @param {String} value The email address
10393          */
10394         'email' : function(v){
10395             return email.test(v);
10396         },
10397         /**
10398          * The error text to display when the email validation function returns false
10399          * @type String
10400          */
10401         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10402         /**
10403          * The keystroke filter mask to be applied on email input
10404          * @type RegExp
10405          */
10406         'emailMask' : /[a-z0-9_\.\-@]/i,
10407
10408         /**
10409          * The function used to validate URLs
10410          * @param {String} value The URL
10411          */
10412         'url' : function(v){
10413             return url.test(v);
10414         },
10415         /**
10416          * The error text to display when the url validation function returns false
10417          * @type String
10418          */
10419         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10420         
10421         /**
10422          * The function used to validate alpha values
10423          * @param {String} value The value
10424          */
10425         'alpha' : function(v){
10426             return alpha.test(v);
10427         },
10428         /**
10429          * The error text to display when the alpha validation function returns false
10430          * @type String
10431          */
10432         'alphaText' : 'This field should only contain letters and _',
10433         /**
10434          * The keystroke filter mask to be applied on alpha input
10435          * @type RegExp
10436          */
10437         'alphaMask' : /[a-z_]/i,
10438
10439         /**
10440          * The function used to validate alphanumeric values
10441          * @param {String} value The value
10442          */
10443         'alphanum' : function(v){
10444             return alphanum.test(v);
10445         },
10446         /**
10447          * The error text to display when the alphanumeric validation function returns false
10448          * @type String
10449          */
10450         'alphanumText' : 'This field should only contain letters, numbers and _',
10451         /**
10452          * The keystroke filter mask to be applied on alphanumeric input
10453          * @type RegExp
10454          */
10455         'alphanumMask' : /[a-z0-9_]/i
10456     };
10457 }();/*
10458  * - LGPL
10459  *
10460  * Input
10461  * 
10462  */
10463
10464 /**
10465  * @class Roo.bootstrap.Input
10466  * @extends Roo.bootstrap.Component
10467  * Bootstrap Input class
10468  * @cfg {Boolean} disabled is it disabled
10469  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10470  * @cfg {String} name name of the input
10471  * @cfg {string} fieldLabel - the label associated
10472  * @cfg {string} placeholder - placeholder to put in text.
10473  * @cfg {string}  before - input group add on before
10474  * @cfg {string} after - input group add on after
10475  * @cfg {string} size - (lg|sm) or leave empty..
10476  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10477  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10478  * @cfg {Number} md colspan out of 12 for computer-sized screens
10479  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10480  * @cfg {string} value default value of the input
10481  * @cfg {Number} labelWidth set the width of label 
10482  * @cfg {Number} labellg set the width of label (1-12)
10483  * @cfg {Number} labelmd set the width of label (1-12)
10484  * @cfg {Number} labelsm set the width of label (1-12)
10485  * @cfg {Number} labelxs set the width of label (1-12)
10486  * @cfg {String} labelAlign (top|left)
10487  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10488  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10489  * @cfg {String} indicatorpos (left|right) default left
10490  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10491  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10492  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10493
10494  * @cfg {String} align (left|center|right) Default left
10495  * @cfg {Boolean} forceFeedback (true|false) Default false
10496  * 
10497  * @constructor
10498  * Create a new Input
10499  * @param {Object} config The config object
10500  */
10501
10502 Roo.bootstrap.Input = function(config){
10503     
10504     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10505     
10506     this.addEvents({
10507         /**
10508          * @event focus
10509          * Fires when this field receives input focus.
10510          * @param {Roo.form.Field} this
10511          */
10512         focus : true,
10513         /**
10514          * @event blur
10515          * Fires when this field loses input focus.
10516          * @param {Roo.form.Field} this
10517          */
10518         blur : true,
10519         /**
10520          * @event specialkey
10521          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10522          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10523          * @param {Roo.form.Field} this
10524          * @param {Roo.EventObject} e The event object
10525          */
10526         specialkey : true,
10527         /**
10528          * @event change
10529          * Fires just before the field blurs if the field value has changed.
10530          * @param {Roo.form.Field} this
10531          * @param {Mixed} newValue The new value
10532          * @param {Mixed} oldValue The original value
10533          */
10534         change : true,
10535         /**
10536          * @event invalid
10537          * Fires after the field has been marked as invalid.
10538          * @param {Roo.form.Field} this
10539          * @param {String} msg The validation message
10540          */
10541         invalid : true,
10542         /**
10543          * @event valid
10544          * Fires after the field has been validated with no errors.
10545          * @param {Roo.form.Field} this
10546          */
10547         valid : true,
10548          /**
10549          * @event keyup
10550          * Fires after the key up
10551          * @param {Roo.form.Field} this
10552          * @param {Roo.EventObject}  e The event Object
10553          */
10554         keyup : true,
10555         /**
10556          * @event paste
10557          * Fires after the user pastes into input
10558          * @param {Roo.form.Field} this
10559          * @param {Roo.EventObject}  e The event Object
10560          */
10561         paste : true
10562     });
10563 };
10564
10565 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10566      /**
10567      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10568       automatic validation (defaults to "keyup").
10569      */
10570     validationEvent : "keyup",
10571      /**
10572      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10573      */
10574     validateOnBlur : true,
10575     /**
10576      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10577      */
10578     validationDelay : 250,
10579      /**
10580      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10581      */
10582     focusClass : "x-form-focus",  // not needed???
10583     
10584        
10585     /**
10586      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10587      */
10588     invalidClass : "has-warning",
10589     
10590     /**
10591      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10592      */
10593     validClass : "has-success",
10594     
10595     /**
10596      * @cfg {Boolean} hasFeedback (true|false) default true
10597      */
10598     hasFeedback : true,
10599     
10600     /**
10601      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10602      */
10603     invalidFeedbackClass : "glyphicon-warning-sign",
10604     
10605     /**
10606      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10607      */
10608     validFeedbackClass : "glyphicon-ok",
10609     
10610     /**
10611      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10612      */
10613     selectOnFocus : false,
10614     
10615      /**
10616      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10617      */
10618     maskRe : null,
10619        /**
10620      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10621      */
10622     vtype : null,
10623     
10624       /**
10625      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10626      */
10627     disableKeyFilter : false,
10628     
10629        /**
10630      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10631      */
10632     disabled : false,
10633      /**
10634      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10635      */
10636     allowBlank : true,
10637     /**
10638      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10639      */
10640     blankText : "Please complete this mandatory field",
10641     
10642      /**
10643      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10644      */
10645     minLength : 0,
10646     /**
10647      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10648      */
10649     maxLength : Number.MAX_VALUE,
10650     /**
10651      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10652      */
10653     minLengthText : "The minimum length for this field is {0}",
10654     /**
10655      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10656      */
10657     maxLengthText : "The maximum length for this field is {0}",
10658   
10659     
10660     /**
10661      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10662      * If available, this function will be called only after the basic validators all return true, and will be passed the
10663      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10664      */
10665     validator : null,
10666     /**
10667      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10668      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10669      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10670      */
10671     regex : null,
10672     /**
10673      * @cfg {String} regexText -- Depricated - use Invalid Text
10674      */
10675     regexText : "",
10676     
10677     /**
10678      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10679      */
10680     invalidText : "",
10681     
10682     
10683     
10684     autocomplete: false,
10685     
10686     
10687     fieldLabel : '',
10688     inputType : 'text',
10689     
10690     name : false,
10691     placeholder: false,
10692     before : false,
10693     after : false,
10694     size : false,
10695     hasFocus : false,
10696     preventMark: false,
10697     isFormField : true,
10698     value : '',
10699     labelWidth : 2,
10700     labelAlign : false,
10701     readOnly : false,
10702     align : false,
10703     formatedValue : false,
10704     forceFeedback : false,
10705     
10706     indicatorpos : 'left',
10707     
10708     labellg : 0,
10709     labelmd : 0,
10710     labelsm : 0,
10711     labelxs : 0,
10712     
10713     capture : '',
10714     accept : '',
10715     
10716     parentLabelAlign : function()
10717     {
10718         var parent = this;
10719         while (parent.parent()) {
10720             parent = parent.parent();
10721             if (typeof(parent.labelAlign) !='undefined') {
10722                 return parent.labelAlign;
10723             }
10724         }
10725         return 'left';
10726         
10727     },
10728     
10729     getAutoCreate : function()
10730     {
10731         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10732         
10733         var id = Roo.id();
10734         
10735         var cfg = {};
10736         
10737         if(this.inputType != 'hidden'){
10738             cfg.cls = 'form-group' //input-group
10739         }
10740         
10741         var input =  {
10742             tag: 'input',
10743             id : id,
10744             type : this.inputType,
10745             value : this.value,
10746             cls : 'form-control',
10747             placeholder : this.placeholder || '',
10748             autocomplete : this.autocomplete || 'new-password'
10749         };
10750         if (this.inputType == 'file') {
10751             input.style = 'overflow:hidden'; // why not in CSS?
10752         }
10753         
10754         if(this.capture.length){
10755             input.capture = this.capture;
10756         }
10757         
10758         if(this.accept.length){
10759             input.accept = this.accept + "/*";
10760         }
10761         
10762         if(this.align){
10763             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10764         }
10765         
10766         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10767             input.maxLength = this.maxLength;
10768         }
10769         
10770         if (this.disabled) {
10771             input.disabled=true;
10772         }
10773         
10774         if (this.readOnly) {
10775             input.readonly=true;
10776         }
10777         
10778         if (this.name) {
10779             input.name = this.name;
10780         }
10781         
10782         if (this.size) {
10783             input.cls += ' input-' + this.size;
10784         }
10785         
10786         var settings=this;
10787         ['xs','sm','md','lg'].map(function(size){
10788             if (settings[size]) {
10789                 cfg.cls += ' col-' + size + '-' + settings[size];
10790             }
10791         });
10792         
10793         var inputblock = input;
10794         
10795         var feedback = {
10796             tag: 'span',
10797             cls: 'glyphicon form-control-feedback'
10798         };
10799             
10800         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10801             
10802             inputblock = {
10803                 cls : 'has-feedback',
10804                 cn :  [
10805                     input,
10806                     feedback
10807                 ] 
10808             };  
10809         }
10810         
10811         if (this.before || this.after) {
10812             
10813             inputblock = {
10814                 cls : 'input-group',
10815                 cn :  [] 
10816             };
10817             
10818             if (this.before && typeof(this.before) == 'string') {
10819                 
10820                 inputblock.cn.push({
10821                     tag :'span',
10822                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10823                     html : this.before
10824                 });
10825             }
10826             if (this.before && typeof(this.before) == 'object') {
10827                 this.before = Roo.factory(this.before);
10828                 
10829                 inputblock.cn.push({
10830                     tag :'span',
10831                     cls : 'roo-input-before input-group-prepend   input-group-' +
10832                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10833                 });
10834             }
10835             
10836             inputblock.cn.push(input);
10837             
10838             if (this.after && typeof(this.after) == 'string') {
10839                 inputblock.cn.push({
10840                     tag :'span',
10841                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10842                     html : this.after
10843                 });
10844             }
10845             if (this.after && typeof(this.after) == 'object') {
10846                 this.after = Roo.factory(this.after);
10847                 
10848                 inputblock.cn.push({
10849                     tag :'span',
10850                     cls : 'roo-input-after input-group-append  input-group-' +
10851                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10852                 });
10853             }
10854             
10855             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10856                 inputblock.cls += ' has-feedback';
10857                 inputblock.cn.push(feedback);
10858             }
10859         };
10860         var indicator = {
10861             tag : 'i',
10862             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10863             tooltip : 'This field is required'
10864         };
10865         if (this.allowBlank ) {
10866             indicator.style = this.allowBlank ? ' display:none' : '';
10867         }
10868         if (align ==='left' && this.fieldLabel.length) {
10869             
10870             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10871             
10872             cfg.cn = [
10873                 indicator,
10874                 {
10875                     tag: 'label',
10876                     'for' :  id,
10877                     cls : 'control-label col-form-label',
10878                     html : this.fieldLabel
10879
10880                 },
10881                 {
10882                     cls : "", 
10883                     cn: [
10884                         inputblock
10885                     ]
10886                 }
10887             ];
10888             
10889             var labelCfg = cfg.cn[1];
10890             var contentCfg = cfg.cn[2];
10891             
10892             if(this.indicatorpos == 'right'){
10893                 cfg.cn = [
10894                     {
10895                         tag: 'label',
10896                         'for' :  id,
10897                         cls : 'control-label col-form-label',
10898                         cn : [
10899                             {
10900                                 tag : 'span',
10901                                 html : this.fieldLabel
10902                             },
10903                             indicator
10904                         ]
10905                     },
10906                     {
10907                         cls : "",
10908                         cn: [
10909                             inputblock
10910                         ]
10911                     }
10912
10913                 ];
10914                 
10915                 labelCfg = cfg.cn[0];
10916                 contentCfg = cfg.cn[1];
10917             
10918             }
10919             
10920             if(this.labelWidth > 12){
10921                 labelCfg.style = "width: " + this.labelWidth + 'px';
10922             }
10923             
10924             if(this.labelWidth < 13 && this.labelmd == 0){
10925                 this.labelmd = this.labelWidth;
10926             }
10927             
10928             if(this.labellg > 0){
10929                 labelCfg.cls += ' col-lg-' + this.labellg;
10930                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10931             }
10932             
10933             if(this.labelmd > 0){
10934                 labelCfg.cls += ' col-md-' + this.labelmd;
10935                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10936             }
10937             
10938             if(this.labelsm > 0){
10939                 labelCfg.cls += ' col-sm-' + this.labelsm;
10940                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10941             }
10942             
10943             if(this.labelxs > 0){
10944                 labelCfg.cls += ' col-xs-' + this.labelxs;
10945                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10946             }
10947             
10948             
10949         } else if ( this.fieldLabel.length) {
10950                 
10951             
10952             
10953             cfg.cn = [
10954                 {
10955                     tag : 'i',
10956                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10957                     tooltip : 'This field is required',
10958                     style : this.allowBlank ? ' display:none' : '' 
10959                 },
10960                 {
10961                     tag: 'label',
10962                    //cls : 'input-group-addon',
10963                     html : this.fieldLabel
10964
10965                 },
10966
10967                inputblock
10968
10969            ];
10970            
10971            if(this.indicatorpos == 'right'){
10972        
10973                 cfg.cn = [
10974                     {
10975                         tag: 'label',
10976                        //cls : 'input-group-addon',
10977                         html : this.fieldLabel
10978
10979                     },
10980                     {
10981                         tag : 'i',
10982                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10983                         tooltip : 'This field is required',
10984                         style : this.allowBlank ? ' display:none' : '' 
10985                     },
10986
10987                    inputblock
10988
10989                ];
10990
10991             }
10992
10993         } else {
10994             
10995             cfg.cn = [
10996
10997                     inputblock
10998
10999             ];
11000                 
11001                 
11002         };
11003         
11004         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11005            cfg.cls += ' navbar-form';
11006         }
11007         
11008         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11009             // on BS4 we do this only if not form 
11010             cfg.cls += ' navbar-form';
11011             cfg.tag = 'li';
11012         }
11013         
11014         return cfg;
11015         
11016     },
11017     /**
11018      * return the real input element.
11019      */
11020     inputEl: function ()
11021     {
11022         return this.el.select('input.form-control',true).first();
11023     },
11024     
11025     tooltipEl : function()
11026     {
11027         return this.inputEl();
11028     },
11029     
11030     indicatorEl : function()
11031     {
11032         if (Roo.bootstrap.version == 4) {
11033             return false; // not enabled in v4 yet.
11034         }
11035         
11036         var indicator = this.el.select('i.roo-required-indicator',true).first();
11037         
11038         if(!indicator){
11039             return false;
11040         }
11041         
11042         return indicator;
11043         
11044     },
11045     
11046     setDisabled : function(v)
11047     {
11048         var i  = this.inputEl().dom;
11049         if (!v) {
11050             i.removeAttribute('disabled');
11051             return;
11052             
11053         }
11054         i.setAttribute('disabled','true');
11055     },
11056     initEvents : function()
11057     {
11058           
11059         this.inputEl().on("keydown" , this.fireKey,  this);
11060         this.inputEl().on("focus", this.onFocus,  this);
11061         this.inputEl().on("blur", this.onBlur,  this);
11062         
11063         this.inputEl().relayEvent('keyup', this);
11064         this.inputEl().relayEvent('paste', this);
11065         
11066         this.indicator = this.indicatorEl();
11067         
11068         if(this.indicator){
11069             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11070         }
11071  
11072         // reference to original value for reset
11073         this.originalValue = this.getValue();
11074         //Roo.form.TextField.superclass.initEvents.call(this);
11075         if(this.validationEvent == 'keyup'){
11076             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11077             this.inputEl().on('keyup', this.filterValidation, this);
11078         }
11079         else if(this.validationEvent !== false){
11080             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11081         }
11082         
11083         if(this.selectOnFocus){
11084             this.on("focus", this.preFocus, this);
11085             
11086         }
11087         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11088             this.inputEl().on("keypress", this.filterKeys, this);
11089         } else {
11090             this.inputEl().relayEvent('keypress', this);
11091         }
11092        /* if(this.grow){
11093             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11094             this.el.on("click", this.autoSize,  this);
11095         }
11096         */
11097         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11098             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11099         }
11100         
11101         if (typeof(this.before) == 'object') {
11102             this.before.render(this.el.select('.roo-input-before',true).first());
11103         }
11104         if (typeof(this.after) == 'object') {
11105             this.after.render(this.el.select('.roo-input-after',true).first());
11106         }
11107         
11108         this.inputEl().on('change', this.onChange, this);
11109         
11110     },
11111     filterValidation : function(e){
11112         if(!e.isNavKeyPress()){
11113             this.validationTask.delay(this.validationDelay);
11114         }
11115     },
11116      /**
11117      * Validates the field value
11118      * @return {Boolean} True if the value is valid, else false
11119      */
11120     validate : function(){
11121         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11122         if(this.disabled || this.validateValue(this.getRawValue())){
11123             this.markValid();
11124             return true;
11125         }
11126         
11127         this.markInvalid();
11128         return false;
11129     },
11130     
11131     
11132     /**
11133      * Validates a value according to the field's validation rules and marks the field as invalid
11134      * if the validation fails
11135      * @param {Mixed} value The value to validate
11136      * @return {Boolean} True if the value is valid, else false
11137      */
11138     validateValue : function(value)
11139     {
11140         if(this.getVisibilityEl().hasClass('hidden')){
11141             return true;
11142         }
11143         
11144         if(value.length < 1)  { // if it's blank
11145             if(this.allowBlank){
11146                 return true;
11147             }
11148             return false;
11149         }
11150         
11151         if(value.length < this.minLength){
11152             return false;
11153         }
11154         if(value.length > this.maxLength){
11155             return false;
11156         }
11157         if(this.vtype){
11158             var vt = Roo.form.VTypes;
11159             if(!vt[this.vtype](value, this)){
11160                 return false;
11161             }
11162         }
11163         if(typeof this.validator == "function"){
11164             var msg = this.validator(value);
11165             if(msg !== true){
11166                 return false;
11167             }
11168             if (typeof(msg) == 'string') {
11169                 this.invalidText = msg;
11170             }
11171         }
11172         
11173         if(this.regex && !this.regex.test(value)){
11174             return false;
11175         }
11176         
11177         return true;
11178     },
11179     
11180      // private
11181     fireKey : function(e){
11182         //Roo.log('field ' + e.getKey());
11183         if(e.isNavKeyPress()){
11184             this.fireEvent("specialkey", this, e);
11185         }
11186     },
11187     focus : function (selectText){
11188         if(this.rendered){
11189             this.inputEl().focus();
11190             if(selectText === true){
11191                 this.inputEl().dom.select();
11192             }
11193         }
11194         return this;
11195     } ,
11196     
11197     onFocus : function(){
11198         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11199            // this.el.addClass(this.focusClass);
11200         }
11201         if(!this.hasFocus){
11202             this.hasFocus = true;
11203             this.startValue = this.getValue();
11204             this.fireEvent("focus", this);
11205         }
11206     },
11207     
11208     beforeBlur : Roo.emptyFn,
11209
11210     
11211     // private
11212     onBlur : function(){
11213         this.beforeBlur();
11214         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11215             //this.el.removeClass(this.focusClass);
11216         }
11217         this.hasFocus = false;
11218         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11219             this.validate();
11220         }
11221         var v = this.getValue();
11222         if(String(v) !== String(this.startValue)){
11223             this.fireEvent('change', this, v, this.startValue);
11224         }
11225         this.fireEvent("blur", this);
11226     },
11227     
11228     onChange : function(e)
11229     {
11230         var v = this.getValue();
11231         if(String(v) !== String(this.startValue)){
11232             this.fireEvent('change', this, v, this.startValue);
11233         }
11234         
11235     },
11236     
11237     /**
11238      * Resets the current field value to the originally loaded value and clears any validation messages
11239      */
11240     reset : function(){
11241         this.setValue(this.originalValue);
11242         this.validate();
11243     },
11244      /**
11245      * Returns the name of the field
11246      * @return {Mixed} name The name field
11247      */
11248     getName: function(){
11249         return this.name;
11250     },
11251      /**
11252      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11253      * @return {Mixed} value The field value
11254      */
11255     getValue : function(){
11256         
11257         var v = this.inputEl().getValue();
11258         
11259         return v;
11260     },
11261     /**
11262      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11263      * @return {Mixed} value The field value
11264      */
11265     getRawValue : function(){
11266         var v = this.inputEl().getValue();
11267         
11268         return v;
11269     },
11270     
11271     /**
11272      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11273      * @param {Mixed} value The value to set
11274      */
11275     setRawValue : function(v){
11276         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11277     },
11278     
11279     selectText : function(start, end){
11280         var v = this.getRawValue();
11281         if(v.length > 0){
11282             start = start === undefined ? 0 : start;
11283             end = end === undefined ? v.length : end;
11284             var d = this.inputEl().dom;
11285             if(d.setSelectionRange){
11286                 d.setSelectionRange(start, end);
11287             }else if(d.createTextRange){
11288                 var range = d.createTextRange();
11289                 range.moveStart("character", start);
11290                 range.moveEnd("character", v.length-end);
11291                 range.select();
11292             }
11293         }
11294     },
11295     
11296     /**
11297      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11298      * @param {Mixed} value The value to set
11299      */
11300     setValue : function(v){
11301         this.value = v;
11302         if(this.rendered){
11303             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11304             this.validate();
11305         }
11306     },
11307     
11308     /*
11309     processValue : function(value){
11310         if(this.stripCharsRe){
11311             var newValue = value.replace(this.stripCharsRe, '');
11312             if(newValue !== value){
11313                 this.setRawValue(newValue);
11314                 return newValue;
11315             }
11316         }
11317         return value;
11318     },
11319   */
11320     preFocus : function(){
11321         
11322         if(this.selectOnFocus){
11323             this.inputEl().dom.select();
11324         }
11325     },
11326     filterKeys : function(e){
11327         var k = e.getKey();
11328         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11329             return;
11330         }
11331         var c = e.getCharCode(), cc = String.fromCharCode(c);
11332         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11333             return;
11334         }
11335         if(!this.maskRe.test(cc)){
11336             e.stopEvent();
11337         }
11338     },
11339      /**
11340      * Clear any invalid styles/messages for this field
11341      */
11342     clearInvalid : function(){
11343         
11344         if(!this.el || this.preventMark){ // not rendered
11345             return;
11346         }
11347         
11348         
11349         this.el.removeClass([this.invalidClass, 'is-invalid']);
11350         
11351         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11352             
11353             var feedback = this.el.select('.form-control-feedback', true).first();
11354             
11355             if(feedback){
11356                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11357             }
11358             
11359         }
11360         
11361         if(this.indicator){
11362             this.indicator.removeClass('visible');
11363             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11364         }
11365         
11366         this.fireEvent('valid', this);
11367     },
11368     
11369      /**
11370      * Mark this field as valid
11371      */
11372     markValid : function()
11373     {
11374         if(!this.el  || this.preventMark){ // not rendered...
11375             return;
11376         }
11377         
11378         this.el.removeClass([this.invalidClass, this.validClass]);
11379         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11380
11381         var feedback = this.el.select('.form-control-feedback', true).first();
11382             
11383         if(feedback){
11384             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11385         }
11386         
11387         if(this.indicator){
11388             this.indicator.removeClass('visible');
11389             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11390         }
11391         
11392         if(this.disabled){
11393             return;
11394         }
11395         
11396            
11397         if(this.allowBlank && !this.getRawValue().length){
11398             return;
11399         }
11400         if (Roo.bootstrap.version == 3) {
11401             this.el.addClass(this.validClass);
11402         } else {
11403             this.inputEl().addClass('is-valid');
11404         }
11405
11406         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11407             
11408             var feedback = this.el.select('.form-control-feedback', true).first();
11409             
11410             if(feedback){
11411                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11412                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11413             }
11414             
11415         }
11416         
11417         this.fireEvent('valid', this);
11418     },
11419     
11420      /**
11421      * Mark this field as invalid
11422      * @param {String} msg The validation message
11423      */
11424     markInvalid : function(msg)
11425     {
11426         if(!this.el  || this.preventMark){ // not rendered
11427             return;
11428         }
11429         
11430         this.el.removeClass([this.invalidClass, this.validClass]);
11431         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11432         
11433         var feedback = this.el.select('.form-control-feedback', true).first();
11434             
11435         if(feedback){
11436             this.el.select('.form-control-feedback', true).first().removeClass(
11437                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11438         }
11439
11440         if(this.disabled){
11441             return;
11442         }
11443         
11444         if(this.allowBlank && !this.getRawValue().length){
11445             return;
11446         }
11447         
11448         if(this.indicator){
11449             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11450             this.indicator.addClass('visible');
11451         }
11452         if (Roo.bootstrap.version == 3) {
11453             this.el.addClass(this.invalidClass);
11454         } else {
11455             this.inputEl().addClass('is-invalid');
11456         }
11457         
11458         
11459         
11460         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11461             
11462             var feedback = this.el.select('.form-control-feedback', true).first();
11463             
11464             if(feedback){
11465                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11466                 
11467                 if(this.getValue().length || this.forceFeedback){
11468                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11469                 }
11470                 
11471             }
11472             
11473         }
11474         
11475         this.fireEvent('invalid', this, msg);
11476     },
11477     // private
11478     SafariOnKeyDown : function(event)
11479     {
11480         // this is a workaround for a password hang bug on chrome/ webkit.
11481         if (this.inputEl().dom.type != 'password') {
11482             return;
11483         }
11484         
11485         var isSelectAll = false;
11486         
11487         if(this.inputEl().dom.selectionEnd > 0){
11488             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11489         }
11490         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11491             event.preventDefault();
11492             this.setValue('');
11493             return;
11494         }
11495         
11496         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11497             
11498             event.preventDefault();
11499             // this is very hacky as keydown always get's upper case.
11500             //
11501             var cc = String.fromCharCode(event.getCharCode());
11502             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11503             
11504         }
11505     },
11506     adjustWidth : function(tag, w){
11507         tag = tag.toLowerCase();
11508         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11509             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11510                 if(tag == 'input'){
11511                     return w + 2;
11512                 }
11513                 if(tag == 'textarea'){
11514                     return w-2;
11515                 }
11516             }else if(Roo.isOpera){
11517                 if(tag == 'input'){
11518                     return w + 2;
11519                 }
11520                 if(tag == 'textarea'){
11521                     return w-2;
11522                 }
11523             }
11524         }
11525         return w;
11526     },
11527     
11528     setFieldLabel : function(v)
11529     {
11530         if(!this.rendered){
11531             return;
11532         }
11533         
11534         if(this.indicatorEl()){
11535             var ar = this.el.select('label > span',true);
11536             
11537             if (ar.elements.length) {
11538                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11539                 this.fieldLabel = v;
11540                 return;
11541             }
11542             
11543             var br = this.el.select('label',true);
11544             
11545             if(br.elements.length) {
11546                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11547                 this.fieldLabel = v;
11548                 return;
11549             }
11550             
11551             Roo.log('Cannot Found any of label > span || label in input');
11552             return;
11553         }
11554         
11555         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11556         this.fieldLabel = v;
11557         
11558         
11559     }
11560 });
11561
11562  
11563 /*
11564  * - LGPL
11565  *
11566  * Input
11567  * 
11568  */
11569
11570 /**
11571  * @class Roo.bootstrap.TextArea
11572  * @extends Roo.bootstrap.Input
11573  * Bootstrap TextArea class
11574  * @cfg {Number} cols Specifies the visible width of a text area
11575  * @cfg {Number} rows Specifies the visible number of lines in a text area
11576  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11577  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11578  * @cfg {string} html text
11579  * 
11580  * @constructor
11581  * Create a new TextArea
11582  * @param {Object} config The config object
11583  */
11584
11585 Roo.bootstrap.TextArea = function(config){
11586     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11587    
11588 };
11589
11590 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11591      
11592     cols : false,
11593     rows : 5,
11594     readOnly : false,
11595     warp : 'soft',
11596     resize : false,
11597     value: false,
11598     html: false,
11599     
11600     getAutoCreate : function(){
11601         
11602         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11603         
11604         var id = Roo.id();
11605         
11606         var cfg = {};
11607         
11608         if(this.inputType != 'hidden'){
11609             cfg.cls = 'form-group' //input-group
11610         }
11611         
11612         var input =  {
11613             tag: 'textarea',
11614             id : id,
11615             warp : this.warp,
11616             rows : this.rows,
11617             value : this.value || '',
11618             html: this.html || '',
11619             cls : 'form-control',
11620             placeholder : this.placeholder || '' 
11621             
11622         };
11623         
11624         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11625             input.maxLength = this.maxLength;
11626         }
11627         
11628         if(this.resize){
11629             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11630         }
11631         
11632         if(this.cols){
11633             input.cols = this.cols;
11634         }
11635         
11636         if (this.readOnly) {
11637             input.readonly = true;
11638         }
11639         
11640         if (this.name) {
11641             input.name = this.name;
11642         }
11643         
11644         if (this.size) {
11645             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11646         }
11647         
11648         var settings=this;
11649         ['xs','sm','md','lg'].map(function(size){
11650             if (settings[size]) {
11651                 cfg.cls += ' col-' + size + '-' + settings[size];
11652             }
11653         });
11654         
11655         var inputblock = input;
11656         
11657         if(this.hasFeedback && !this.allowBlank){
11658             
11659             var feedback = {
11660                 tag: 'span',
11661                 cls: 'glyphicon form-control-feedback'
11662             };
11663
11664             inputblock = {
11665                 cls : 'has-feedback',
11666                 cn :  [
11667                     input,
11668                     feedback
11669                 ] 
11670             };  
11671         }
11672         
11673         
11674         if (this.before || this.after) {
11675             
11676             inputblock = {
11677                 cls : 'input-group',
11678                 cn :  [] 
11679             };
11680             if (this.before) {
11681                 inputblock.cn.push({
11682                     tag :'span',
11683                     cls : 'input-group-addon',
11684                     html : this.before
11685                 });
11686             }
11687             
11688             inputblock.cn.push(input);
11689             
11690             if(this.hasFeedback && !this.allowBlank){
11691                 inputblock.cls += ' has-feedback';
11692                 inputblock.cn.push(feedback);
11693             }
11694             
11695             if (this.after) {
11696                 inputblock.cn.push({
11697                     tag :'span',
11698                     cls : 'input-group-addon',
11699                     html : this.after
11700                 });
11701             }
11702             
11703         }
11704         
11705         if (align ==='left' && this.fieldLabel.length) {
11706             cfg.cn = [
11707                 {
11708                     tag: 'label',
11709                     'for' :  id,
11710                     cls : 'control-label',
11711                     html : this.fieldLabel
11712                 },
11713                 {
11714                     cls : "",
11715                     cn: [
11716                         inputblock
11717                     ]
11718                 }
11719
11720             ];
11721             
11722             if(this.labelWidth > 12){
11723                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11724             }
11725
11726             if(this.labelWidth < 13 && this.labelmd == 0){
11727                 this.labelmd = this.labelWidth;
11728             }
11729
11730             if(this.labellg > 0){
11731                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11732                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11733             }
11734
11735             if(this.labelmd > 0){
11736                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11737                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11738             }
11739
11740             if(this.labelsm > 0){
11741                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11742                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11743             }
11744
11745             if(this.labelxs > 0){
11746                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11747                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11748             }
11749             
11750         } else if ( this.fieldLabel.length) {
11751             cfg.cn = [
11752
11753                {
11754                    tag: 'label',
11755                    //cls : 'input-group-addon',
11756                    html : this.fieldLabel
11757
11758                },
11759
11760                inputblock
11761
11762            ];
11763
11764         } else {
11765
11766             cfg.cn = [
11767
11768                 inputblock
11769
11770             ];
11771                 
11772         }
11773         
11774         if (this.disabled) {
11775             input.disabled=true;
11776         }
11777         
11778         return cfg;
11779         
11780     },
11781     /**
11782      * return the real textarea element.
11783      */
11784     inputEl: function ()
11785     {
11786         return this.el.select('textarea.form-control',true).first();
11787     },
11788     
11789     /**
11790      * Clear any invalid styles/messages for this field
11791      */
11792     clearInvalid : function()
11793     {
11794         
11795         if(!this.el || this.preventMark){ // not rendered
11796             return;
11797         }
11798         
11799         var label = this.el.select('label', true).first();
11800         var icon = this.el.select('i.fa-star', true).first();
11801         
11802         if(label && icon){
11803             icon.remove();
11804         }
11805         this.el.removeClass( this.validClass);
11806         this.inputEl().removeClass('is-invalid');
11807          
11808         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11809             
11810             var feedback = this.el.select('.form-control-feedback', true).first();
11811             
11812             if(feedback){
11813                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11814             }
11815             
11816         }
11817         
11818         this.fireEvent('valid', this);
11819     },
11820     
11821      /**
11822      * Mark this field as valid
11823      */
11824     markValid : function()
11825     {
11826         if(!this.el  || this.preventMark){ // not rendered
11827             return;
11828         }
11829         
11830         this.el.removeClass([this.invalidClass, this.validClass]);
11831         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11832         
11833         var feedback = this.el.select('.form-control-feedback', true).first();
11834             
11835         if(feedback){
11836             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11837         }
11838
11839         if(this.disabled || this.allowBlank){
11840             return;
11841         }
11842         
11843         var label = this.el.select('label', true).first();
11844         var icon = this.el.select('i.fa-star', true).first();
11845         
11846         if(label && icon){
11847             icon.remove();
11848         }
11849         if (Roo.bootstrap.version == 3) {
11850             this.el.addClass(this.validClass);
11851         } else {
11852             this.inputEl().addClass('is-valid');
11853         }
11854         
11855         
11856         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11857             
11858             var feedback = this.el.select('.form-control-feedback', true).first();
11859             
11860             if(feedback){
11861                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11862                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11863             }
11864             
11865         }
11866         
11867         this.fireEvent('valid', this);
11868     },
11869     
11870      /**
11871      * Mark this field as invalid
11872      * @param {String} msg The validation message
11873      */
11874     markInvalid : function(msg)
11875     {
11876         if(!this.el  || this.preventMark){ // not rendered
11877             return;
11878         }
11879         
11880         this.el.removeClass([this.invalidClass, this.validClass]);
11881         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11882         
11883         var feedback = this.el.select('.form-control-feedback', true).first();
11884             
11885         if(feedback){
11886             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11887         }
11888
11889         if(this.disabled || this.allowBlank){
11890             return;
11891         }
11892         
11893         var label = this.el.select('label', true).first();
11894         var icon = this.el.select('i.fa-star', true).first();
11895         
11896         if(!this.getValue().length && label && !icon){
11897             this.el.createChild({
11898                 tag : 'i',
11899                 cls : 'text-danger fa fa-lg fa-star',
11900                 tooltip : 'This field is required',
11901                 style : 'margin-right:5px;'
11902             }, label, true);
11903         }
11904         
11905         if (Roo.bootstrap.version == 3) {
11906             this.el.addClass(this.invalidClass);
11907         } else {
11908             this.inputEl().addClass('is-invalid');
11909         }
11910         
11911         // fixme ... this may be depricated need to test..
11912         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11913             
11914             var feedback = this.el.select('.form-control-feedback', true).first();
11915             
11916             if(feedback){
11917                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11918                 
11919                 if(this.getValue().length || this.forceFeedback){
11920                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11921                 }
11922                 
11923             }
11924             
11925         }
11926         
11927         this.fireEvent('invalid', this, msg);
11928     }
11929 });
11930
11931  
11932 /*
11933  * - LGPL
11934  *
11935  * trigger field - base class for combo..
11936  * 
11937  */
11938  
11939 /**
11940  * @class Roo.bootstrap.TriggerField
11941  * @extends Roo.bootstrap.Input
11942  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11943  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11944  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11945  * for which you can provide a custom implementation.  For example:
11946  * <pre><code>
11947 var trigger = new Roo.bootstrap.TriggerField();
11948 trigger.onTriggerClick = myTriggerFn;
11949 trigger.applyTo('my-field');
11950 </code></pre>
11951  *
11952  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11953  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11954  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11955  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11956  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11957
11958  * @constructor
11959  * Create a new TriggerField.
11960  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11961  * to the base TextField)
11962  */
11963 Roo.bootstrap.TriggerField = function(config){
11964     this.mimicing = false;
11965     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11966 };
11967
11968 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11969     /**
11970      * @cfg {String} triggerClass A CSS class to apply to the trigger
11971      */
11972      /**
11973      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11974      */
11975     hideTrigger:false,
11976
11977     /**
11978      * @cfg {Boolean} removable (true|false) special filter default false
11979      */
11980     removable : false,
11981     
11982     /** @cfg {Boolean} grow @hide */
11983     /** @cfg {Number} growMin @hide */
11984     /** @cfg {Number} growMax @hide */
11985
11986     /**
11987      * @hide 
11988      * @method
11989      */
11990     autoSize: Roo.emptyFn,
11991     // private
11992     monitorTab : true,
11993     // private
11994     deferHeight : true,
11995
11996     
11997     actionMode : 'wrap',
11998     
11999     caret : false,
12000     
12001     
12002     getAutoCreate : function(){
12003        
12004         var align = this.labelAlign || this.parentLabelAlign();
12005         
12006         var id = Roo.id();
12007         
12008         var cfg = {
12009             cls: 'form-group' //input-group
12010         };
12011         
12012         
12013         var input =  {
12014             tag: 'input',
12015             id : id,
12016             type : this.inputType,
12017             cls : 'form-control',
12018             autocomplete: 'new-password',
12019             placeholder : this.placeholder || '' 
12020             
12021         };
12022         if (this.name) {
12023             input.name = this.name;
12024         }
12025         if (this.size) {
12026             input.cls += ' input-' + this.size;
12027         }
12028         
12029         if (this.disabled) {
12030             input.disabled=true;
12031         }
12032         
12033         var inputblock = input;
12034         
12035         if(this.hasFeedback && !this.allowBlank){
12036             
12037             var feedback = {
12038                 tag: 'span',
12039                 cls: 'glyphicon form-control-feedback'
12040             };
12041             
12042             if(this.removable && !this.editable  ){
12043                 inputblock = {
12044                     cls : 'has-feedback',
12045                     cn :  [
12046                         inputblock,
12047                         {
12048                             tag: 'button',
12049                             html : 'x',
12050                             cls : 'roo-combo-removable-btn close'
12051                         },
12052                         feedback
12053                     ] 
12054                 };
12055             } else {
12056                 inputblock = {
12057                     cls : 'has-feedback',
12058                     cn :  [
12059                         inputblock,
12060                         feedback
12061                     ] 
12062                 };
12063             }
12064
12065         } else {
12066             if(this.removable && !this.editable ){
12067                 inputblock = {
12068                     cls : 'roo-removable',
12069                     cn :  [
12070                         inputblock,
12071                         {
12072                             tag: 'button',
12073                             html : 'x',
12074                             cls : 'roo-combo-removable-btn close'
12075                         }
12076                     ] 
12077                 };
12078             }
12079         }
12080         
12081         if (this.before || this.after) {
12082             
12083             inputblock = {
12084                 cls : 'input-group',
12085                 cn :  [] 
12086             };
12087             if (this.before) {
12088                 inputblock.cn.push({
12089                     tag :'span',
12090                     cls : 'input-group-addon input-group-prepend input-group-text',
12091                     html : this.before
12092                 });
12093             }
12094             
12095             inputblock.cn.push(input);
12096             
12097             if(this.hasFeedback && !this.allowBlank){
12098                 inputblock.cls += ' has-feedback';
12099                 inputblock.cn.push(feedback);
12100             }
12101             
12102             if (this.after) {
12103                 inputblock.cn.push({
12104                     tag :'span',
12105                     cls : 'input-group-addon input-group-append input-group-text',
12106                     html : this.after
12107                 });
12108             }
12109             
12110         };
12111         
12112       
12113         
12114         var ibwrap = inputblock;
12115         
12116         if(this.multiple){
12117             ibwrap = {
12118                 tag: 'ul',
12119                 cls: 'roo-select2-choices',
12120                 cn:[
12121                     {
12122                         tag: 'li',
12123                         cls: 'roo-select2-search-field',
12124                         cn: [
12125
12126                             inputblock
12127                         ]
12128                     }
12129                 ]
12130             };
12131                 
12132         }
12133         
12134         var combobox = {
12135             cls: 'roo-select2-container input-group',
12136             cn: [
12137                  {
12138                     tag: 'input',
12139                     type : 'hidden',
12140                     cls: 'form-hidden-field'
12141                 },
12142                 ibwrap
12143             ]
12144         };
12145         
12146         if(!this.multiple && this.showToggleBtn){
12147             
12148             var caret = {
12149                         tag: 'span',
12150                         cls: 'caret'
12151              };
12152             if (this.caret != false) {
12153                 caret = {
12154                      tag: 'i',
12155                      cls: 'fa fa-' + this.caret
12156                 };
12157                 
12158             }
12159             
12160             combobox.cn.push({
12161                 tag :'span',
12162                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12163                 cn : [
12164                     Roo.bootstrap.version == 3 ? caret : '',
12165                     {
12166                         tag: 'span',
12167                         cls: 'combobox-clear',
12168                         cn  : [
12169                             {
12170                                 tag : 'i',
12171                                 cls: 'icon-remove'
12172                             }
12173                         ]
12174                     }
12175                 ]
12176
12177             })
12178         }
12179         
12180         if(this.multiple){
12181             combobox.cls += ' roo-select2-container-multi';
12182         }
12183          var indicator = {
12184             tag : 'i',
12185             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12186             tooltip : 'This field is required'
12187         };
12188         if (Roo.bootstrap.version == 4) {
12189             indicator = {
12190                 tag : 'i',
12191                 style : 'display:none'
12192             };
12193         }
12194         
12195         
12196         if (align ==='left' && this.fieldLabel.length) {
12197             
12198             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12199
12200             cfg.cn = [
12201                 indicator,
12202                 {
12203                     tag: 'label',
12204                     'for' :  id,
12205                     cls : 'control-label',
12206                     html : this.fieldLabel
12207
12208                 },
12209                 {
12210                     cls : "", 
12211                     cn: [
12212                         combobox
12213                     ]
12214                 }
12215
12216             ];
12217             
12218             var labelCfg = cfg.cn[1];
12219             var contentCfg = cfg.cn[2];
12220             
12221             if(this.indicatorpos == 'right'){
12222                 cfg.cn = [
12223                     {
12224                         tag: 'label',
12225                         'for' :  id,
12226                         cls : 'control-label',
12227                         cn : [
12228                             {
12229                                 tag : 'span',
12230                                 html : this.fieldLabel
12231                             },
12232                             indicator
12233                         ]
12234                     },
12235                     {
12236                         cls : "", 
12237                         cn: [
12238                             combobox
12239                         ]
12240                     }
12241
12242                 ];
12243                 
12244                 labelCfg = cfg.cn[0];
12245                 contentCfg = cfg.cn[1];
12246             }
12247             
12248             if(this.labelWidth > 12){
12249                 labelCfg.style = "width: " + this.labelWidth + 'px';
12250             }
12251             
12252             if(this.labelWidth < 13 && this.labelmd == 0){
12253                 this.labelmd = this.labelWidth;
12254             }
12255             
12256             if(this.labellg > 0){
12257                 labelCfg.cls += ' col-lg-' + this.labellg;
12258                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12259             }
12260             
12261             if(this.labelmd > 0){
12262                 labelCfg.cls += ' col-md-' + this.labelmd;
12263                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12264             }
12265             
12266             if(this.labelsm > 0){
12267                 labelCfg.cls += ' col-sm-' + this.labelsm;
12268                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12269             }
12270             
12271             if(this.labelxs > 0){
12272                 labelCfg.cls += ' col-xs-' + this.labelxs;
12273                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12274             }
12275             
12276         } else if ( this.fieldLabel.length) {
12277 //                Roo.log(" label");
12278             cfg.cn = [
12279                 indicator,
12280                {
12281                    tag: 'label',
12282                    //cls : 'input-group-addon',
12283                    html : this.fieldLabel
12284
12285                },
12286
12287                combobox
12288
12289             ];
12290             
12291             if(this.indicatorpos == 'right'){
12292                 
12293                 cfg.cn = [
12294                     {
12295                        tag: 'label',
12296                        cn : [
12297                            {
12298                                tag : 'span',
12299                                html : this.fieldLabel
12300                            },
12301                            indicator
12302                        ]
12303
12304                     },
12305                     combobox
12306
12307                 ];
12308
12309             }
12310
12311         } else {
12312             
12313 //                Roo.log(" no label && no align");
12314                 cfg = combobox
12315                      
12316                 
12317         }
12318         
12319         var settings=this;
12320         ['xs','sm','md','lg'].map(function(size){
12321             if (settings[size]) {
12322                 cfg.cls += ' col-' + size + '-' + settings[size];
12323             }
12324         });
12325         
12326         return cfg;
12327         
12328     },
12329     
12330     
12331     
12332     // private
12333     onResize : function(w, h){
12334 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12335 //        if(typeof w == 'number'){
12336 //            var x = w - this.trigger.getWidth();
12337 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12338 //            this.trigger.setStyle('left', x+'px');
12339 //        }
12340     },
12341
12342     // private
12343     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12344
12345     // private
12346     getResizeEl : function(){
12347         return this.inputEl();
12348     },
12349
12350     // private
12351     getPositionEl : function(){
12352         return this.inputEl();
12353     },
12354
12355     // private
12356     alignErrorIcon : function(){
12357         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12358     },
12359
12360     // private
12361     initEvents : function(){
12362         
12363         this.createList();
12364         
12365         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12366         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12367         if(!this.multiple && this.showToggleBtn){
12368             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12369             if(this.hideTrigger){
12370                 this.trigger.setDisplayed(false);
12371             }
12372             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12373         }
12374         
12375         if(this.multiple){
12376             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12377         }
12378         
12379         if(this.removable && !this.editable && !this.tickable){
12380             var close = this.closeTriggerEl();
12381             
12382             if(close){
12383                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12384                 close.on('click', this.removeBtnClick, this, close);
12385             }
12386         }
12387         
12388         //this.trigger.addClassOnOver('x-form-trigger-over');
12389         //this.trigger.addClassOnClick('x-form-trigger-click');
12390         
12391         //if(!this.width){
12392         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12393         //}
12394     },
12395     
12396     closeTriggerEl : function()
12397     {
12398         var close = this.el.select('.roo-combo-removable-btn', true).first();
12399         return close ? close : false;
12400     },
12401     
12402     removeBtnClick : function(e, h, el)
12403     {
12404         e.preventDefault();
12405         
12406         if(this.fireEvent("remove", this) !== false){
12407             this.reset();
12408             this.fireEvent("afterremove", this)
12409         }
12410     },
12411     
12412     createList : function()
12413     {
12414         this.list = Roo.get(document.body).createChild({
12415             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12416             cls: 'typeahead typeahead-long dropdown-menu shadow',
12417             style: 'display:none'
12418         });
12419         
12420         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12421         
12422     },
12423
12424     // private
12425     initTrigger : function(){
12426        
12427     },
12428
12429     // private
12430     onDestroy : function(){
12431         if(this.trigger){
12432             this.trigger.removeAllListeners();
12433           //  this.trigger.remove();
12434         }
12435         //if(this.wrap){
12436         //    this.wrap.remove();
12437         //}
12438         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12439     },
12440
12441     // private
12442     onFocus : function(){
12443         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12444         /*
12445         if(!this.mimicing){
12446             this.wrap.addClass('x-trigger-wrap-focus');
12447             this.mimicing = true;
12448             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12449             if(this.monitorTab){
12450                 this.el.on("keydown", this.checkTab, this);
12451             }
12452         }
12453         */
12454     },
12455
12456     // private
12457     checkTab : function(e){
12458         if(e.getKey() == e.TAB){
12459             this.triggerBlur();
12460         }
12461     },
12462
12463     // private
12464     onBlur : function(){
12465         // do nothing
12466     },
12467
12468     // private
12469     mimicBlur : function(e, t){
12470         /*
12471         if(!this.wrap.contains(t) && this.validateBlur()){
12472             this.triggerBlur();
12473         }
12474         */
12475     },
12476
12477     // private
12478     triggerBlur : function(){
12479         this.mimicing = false;
12480         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12481         if(this.monitorTab){
12482             this.el.un("keydown", this.checkTab, this);
12483         }
12484         //this.wrap.removeClass('x-trigger-wrap-focus');
12485         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12486     },
12487
12488     // private
12489     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12490     validateBlur : function(e, t){
12491         return true;
12492     },
12493
12494     // private
12495     onDisable : function(){
12496         this.inputEl().dom.disabled = true;
12497         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12498         //if(this.wrap){
12499         //    this.wrap.addClass('x-item-disabled');
12500         //}
12501     },
12502
12503     // private
12504     onEnable : function(){
12505         this.inputEl().dom.disabled = false;
12506         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12507         //if(this.wrap){
12508         //    this.el.removeClass('x-item-disabled');
12509         //}
12510     },
12511
12512     // private
12513     onShow : function(){
12514         var ae = this.getActionEl();
12515         
12516         if(ae){
12517             ae.dom.style.display = '';
12518             ae.dom.style.visibility = 'visible';
12519         }
12520     },
12521
12522     // private
12523     
12524     onHide : function(){
12525         var ae = this.getActionEl();
12526         ae.dom.style.display = 'none';
12527     },
12528
12529     /**
12530      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12531      * by an implementing function.
12532      * @method
12533      * @param {EventObject} e
12534      */
12535     onTriggerClick : Roo.emptyFn
12536 });
12537  
12538 /*
12539 * Licence: LGPL
12540 */
12541
12542 /**
12543  * @class Roo.bootstrap.CardUploader
12544  * @extends Roo.bootstrap.Button
12545  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12546  * @cfg {Number} errorTimeout default 3000
12547  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12548  * @cfg {Array}  html The button text.
12549
12550  *
12551  * @constructor
12552  * Create a new CardUploader
12553  * @param {Object} config The config object
12554  */
12555
12556 Roo.bootstrap.CardUploader = function(config){
12557     
12558  
12559     
12560     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12561     
12562     
12563     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12564         return r.data.id
12565      });
12566     
12567      this.addEvents({
12568          // raw events
12569         /**
12570          * @event preview
12571          * When a image is clicked on - and needs to display a slideshow or similar..
12572          * @param {Roo.bootstrap.Card} this
12573          * @param {Object} The image information data 
12574          *
12575          */
12576         'preview' : true,
12577          /**
12578          * @event download
12579          * When a the download link is clicked
12580          * @param {Roo.bootstrap.Card} this
12581          * @param {Object} The image information data  contains 
12582          */
12583         'download' : true
12584         
12585     });
12586 };
12587  
12588 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12589     
12590      
12591     errorTimeout : 3000,
12592      
12593     images : false,
12594    
12595     fileCollection : false,
12596     allowBlank : true,
12597     
12598     getAutoCreate : function()
12599     {
12600         
12601         var cfg =  {
12602             cls :'form-group' ,
12603             cn : [
12604                
12605                 {
12606                     tag: 'label',
12607                    //cls : 'input-group-addon',
12608                     html : this.fieldLabel
12609
12610                 },
12611
12612                 {
12613                     tag: 'input',
12614                     type : 'hidden',
12615                     name : this.name,
12616                     value : this.value,
12617                     cls : 'd-none  form-control'
12618                 },
12619                 
12620                 {
12621                     tag: 'input',
12622                     multiple : 'multiple',
12623                     type : 'file',
12624                     cls : 'd-none  roo-card-upload-selector'
12625                 },
12626                 
12627                 {
12628                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12629                 },
12630                 {
12631                     cls : 'card-columns roo-card-uploader-container'
12632                 }
12633
12634             ]
12635         };
12636            
12637          
12638         return cfg;
12639     },
12640     
12641     getChildContainer : function() /// what children are added to.
12642     {
12643         return this.containerEl;
12644     },
12645    
12646     getButtonContainer : function() /// what children are added to.
12647     {
12648         return this.el.select(".roo-card-uploader-button-container").first();
12649     },
12650    
12651     initEvents : function()
12652     {
12653         
12654         Roo.bootstrap.Input.prototype.initEvents.call(this);
12655         
12656         var t = this;
12657         this.addxtype({
12658             xns: Roo.bootstrap,
12659
12660             xtype : 'Button',
12661             container_method : 'getButtonContainer' ,            
12662             html :  this.html, // fix changable?
12663             cls : 'w-100 ',
12664             listeners : {
12665                 'click' : function(btn, e) {
12666                     t.onClick(e);
12667                 }
12668             }
12669         });
12670         
12671         
12672         
12673         
12674         this.urlAPI = (window.createObjectURL && window) || 
12675                                 (window.URL && URL.revokeObjectURL && URL) || 
12676                                 (window.webkitURL && webkitURL);
12677                         
12678          
12679          
12680          
12681         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12682         
12683         this.selectorEl.on('change', this.onFileSelected, this);
12684         if (this.images) {
12685             var t = this;
12686             this.images.forEach(function(img) {
12687                 t.addCard(img)
12688             });
12689             this.images = false;
12690         }
12691         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12692          
12693        
12694     },
12695     
12696    
12697     onClick : function(e)
12698     {
12699         e.preventDefault();
12700          
12701         this.selectorEl.dom.click();
12702          
12703     },
12704     
12705     onFileSelected : function(e)
12706     {
12707         e.preventDefault();
12708         
12709         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12710             return;
12711         }
12712         
12713         Roo.each(this.selectorEl.dom.files, function(file){    
12714             this.addFile(file);
12715         }, this);
12716          
12717     },
12718     
12719       
12720     
12721       
12722     
12723     addFile : function(file)
12724     {
12725            
12726         if(typeof(file) === 'string'){
12727             throw "Add file by name?"; // should not happen
12728             return;
12729         }
12730         
12731         if(!file || !this.urlAPI){
12732             return;
12733         }
12734         
12735         // file;
12736         // file.type;
12737         
12738         var _this = this;
12739         
12740         
12741         var url = _this.urlAPI.createObjectURL( file);
12742            
12743         this.addCard({
12744             id : Roo.bootstrap.CardUploader.ID--,
12745             is_uploaded : false,
12746             src : url,
12747             srcfile : file,
12748             title : file.name,
12749             mimetype : file.type,
12750             preview : false,
12751             is_deleted : 0
12752         });
12753         
12754     },
12755     
12756     /**
12757      * addCard - add an Attachment to the uploader
12758      * @param data - the data about the image to upload
12759      *
12760      * {
12761           id : 123
12762           title : "Title of file",
12763           is_uploaded : false,
12764           src : "http://.....",
12765           srcfile : { the File upload object },
12766           mimetype : file.type,
12767           preview : false,
12768           is_deleted : 0
12769           .. any other data...
12770         }
12771      *
12772      * 
12773     */
12774     
12775     addCard : function (data)
12776     {
12777         // hidden input element?
12778         // if the file is not an image...
12779         //then we need to use something other that and header_image
12780         var t = this;
12781         //   remove.....
12782         var footer = [
12783             {
12784                 xns : Roo.bootstrap,
12785                 xtype : 'CardFooter',
12786                  items: [
12787                     {
12788                         xns : Roo.bootstrap,
12789                         xtype : 'Element',
12790                         cls : 'd-flex',
12791                         items : [
12792                             
12793                             {
12794                                 xns : Roo.bootstrap,
12795                                 xtype : 'Button',
12796                                 html : String.format("<small>{0}</small>", data.title),
12797                                 cls : 'col-10 text-left',
12798                                 size: 'sm',
12799                                 weight: 'link',
12800                                 fa : 'download',
12801                                 listeners : {
12802                                     click : function() {
12803                                      
12804                                         t.fireEvent( "download", t, data );
12805                                     }
12806                                 }
12807                             },
12808                           
12809                             {
12810                                 xns : Roo.bootstrap,
12811                                 xtype : 'Button',
12812                                 style: 'max-height: 28px; ',
12813                                 size : 'sm',
12814                                 weight: 'danger',
12815                                 cls : 'col-2',
12816                                 fa : 'times',
12817                                 listeners : {
12818                                     click : function() {
12819                                         t.removeCard(data.id)
12820                                     }
12821                                 }
12822                             }
12823                         ]
12824                     }
12825                     
12826                 ] 
12827             }
12828             
12829         ];
12830         
12831         var cn = this.addxtype(
12832             {
12833                  
12834                 xns : Roo.bootstrap,
12835                 xtype : 'Card',
12836                 closeable : true,
12837                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12838                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12839                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12840                 data : data,
12841                 html : false,
12842                  
12843                 items : footer,
12844                 initEvents : function() {
12845                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12846                     var card = this;
12847                     this.imgEl = this.el.select('.card-img-top').first();
12848                     if (this.imgEl) {
12849                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12850                         this.imgEl.set({ 'pointer' : 'cursor' });
12851                                   
12852                     }
12853                     this.getCardFooter().addClass('p-1');
12854                     
12855                   
12856                 }
12857                 
12858             }
12859         );
12860         // dont' really need ot update items.
12861         // this.items.push(cn);
12862         this.fileCollection.add(cn);
12863         
12864         if (!data.srcfile) {
12865             this.updateInput();
12866             return;
12867         }
12868             
12869         var _t = this;
12870         var reader = new FileReader();
12871         reader.addEventListener("load", function() {  
12872             data.srcdata =  reader.result;
12873             _t.updateInput();
12874         });
12875         reader.readAsDataURL(data.srcfile);
12876         
12877         
12878         
12879     },
12880     removeCard : function(id)
12881     {
12882         
12883         var card  = this.fileCollection.get(id);
12884         card.data.is_deleted = 1;
12885         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12886         //this.fileCollection.remove(card);
12887         //this.items = this.items.filter(function(e) { return e != card });
12888         // dont' really need ot update items.
12889         card.el.dom.parentNode.removeChild(card.el.dom);
12890         this.updateInput();
12891
12892         
12893     },
12894     reset: function()
12895     {
12896         this.fileCollection.each(function(card) {
12897             if (card.el.dom && card.el.dom.parentNode) {
12898                 card.el.dom.parentNode.removeChild(card.el.dom);
12899             }
12900         });
12901         this.fileCollection.clear();
12902         this.updateInput();
12903     },
12904     
12905     updateInput : function()
12906     {
12907          var data = [];
12908         this.fileCollection.each(function(e) {
12909             data.push(e.data);
12910             
12911         });
12912         this.inputEl().dom.value = JSON.stringify(data);
12913         
12914         
12915         
12916     }
12917     
12918     
12919 });
12920
12921
12922 Roo.bootstrap.CardUploader.ID = -1;/*
12923  * Based on:
12924  * Ext JS Library 1.1.1
12925  * Copyright(c) 2006-2007, Ext JS, LLC.
12926  *
12927  * Originally Released Under LGPL - original licence link has changed is not relivant.
12928  *
12929  * Fork - LGPL
12930  * <script type="text/javascript">
12931  */
12932
12933
12934 /**
12935  * @class Roo.data.SortTypes
12936  * @singleton
12937  * Defines the default sorting (casting?) comparison functions used when sorting data.
12938  */
12939 Roo.data.SortTypes = {
12940     /**
12941      * Default sort that does nothing
12942      * @param {Mixed} s The value being converted
12943      * @return {Mixed} The comparison value
12944      */
12945     none : function(s){
12946         return s;
12947     },
12948     
12949     /**
12950      * The regular expression used to strip tags
12951      * @type {RegExp}
12952      * @property
12953      */
12954     stripTagsRE : /<\/?[^>]+>/gi,
12955     
12956     /**
12957      * Strips all HTML tags to sort on text only
12958      * @param {Mixed} s The value being converted
12959      * @return {String} The comparison value
12960      */
12961     asText : function(s){
12962         return String(s).replace(this.stripTagsRE, "");
12963     },
12964     
12965     /**
12966      * Strips all HTML tags to sort on text only - Case insensitive
12967      * @param {Mixed} s The value being converted
12968      * @return {String} The comparison value
12969      */
12970     asUCText : function(s){
12971         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12972     },
12973     
12974     /**
12975      * Case insensitive string
12976      * @param {Mixed} s The value being converted
12977      * @return {String} The comparison value
12978      */
12979     asUCString : function(s) {
12980         return String(s).toUpperCase();
12981     },
12982     
12983     /**
12984      * Date sorting
12985      * @param {Mixed} s The value being converted
12986      * @return {Number} The comparison value
12987      */
12988     asDate : function(s) {
12989         if(!s){
12990             return 0;
12991         }
12992         if(s instanceof Date){
12993             return s.getTime();
12994         }
12995         return Date.parse(String(s));
12996     },
12997     
12998     /**
12999      * Float sorting
13000      * @param {Mixed} s The value being converted
13001      * @return {Float} The comparison value
13002      */
13003     asFloat : function(s) {
13004         var val = parseFloat(String(s).replace(/,/g, ""));
13005         if(isNaN(val)) {
13006             val = 0;
13007         }
13008         return val;
13009     },
13010     
13011     /**
13012      * Integer sorting
13013      * @param {Mixed} s The value being converted
13014      * @return {Number} The comparison value
13015      */
13016     asInt : function(s) {
13017         var val = parseInt(String(s).replace(/,/g, ""));
13018         if(isNaN(val)) {
13019             val = 0;
13020         }
13021         return val;
13022     }
13023 };/*
13024  * Based on:
13025  * Ext JS Library 1.1.1
13026  * Copyright(c) 2006-2007, Ext JS, LLC.
13027  *
13028  * Originally Released Under LGPL - original licence link has changed is not relivant.
13029  *
13030  * Fork - LGPL
13031  * <script type="text/javascript">
13032  */
13033
13034 /**
13035 * @class Roo.data.Record
13036  * Instances of this class encapsulate both record <em>definition</em> information, and record
13037  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13038  * to access Records cached in an {@link Roo.data.Store} object.<br>
13039  * <p>
13040  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13041  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13042  * objects.<br>
13043  * <p>
13044  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13045  * @constructor
13046  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13047  * {@link #create}. The parameters are the same.
13048  * @param {Array} data An associative Array of data values keyed by the field name.
13049  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13050  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13051  * not specified an integer id is generated.
13052  */
13053 Roo.data.Record = function(data, id){
13054     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13055     this.data = data;
13056 };
13057
13058 /**
13059  * Generate a constructor for a specific record layout.
13060  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13061  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13062  * Each field definition object may contain the following properties: <ul>
13063  * <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,
13064  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13065  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13066  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13067  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13068  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13069  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13070  * this may be omitted.</p></li>
13071  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13072  * <ul><li>auto (Default, implies no conversion)</li>
13073  * <li>string</li>
13074  * <li>int</li>
13075  * <li>float</li>
13076  * <li>boolean</li>
13077  * <li>date</li></ul></p></li>
13078  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13079  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13080  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13081  * by the Reader into an object that will be stored in the Record. It is passed the
13082  * following parameters:<ul>
13083  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13084  * </ul></p></li>
13085  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13086  * </ul>
13087  * <br>usage:<br><pre><code>
13088 var TopicRecord = Roo.data.Record.create(
13089     {name: 'title', mapping: 'topic_title'},
13090     {name: 'author', mapping: 'username'},
13091     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13092     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13093     {name: 'lastPoster', mapping: 'user2'},
13094     {name: 'excerpt', mapping: 'post_text'}
13095 );
13096
13097 var myNewRecord = new TopicRecord({
13098     title: 'Do my job please',
13099     author: 'noobie',
13100     totalPosts: 1,
13101     lastPost: new Date(),
13102     lastPoster: 'Animal',
13103     excerpt: 'No way dude!'
13104 });
13105 myStore.add(myNewRecord);
13106 </code></pre>
13107  * @method create
13108  * @static
13109  */
13110 Roo.data.Record.create = function(o){
13111     var f = function(){
13112         f.superclass.constructor.apply(this, arguments);
13113     };
13114     Roo.extend(f, Roo.data.Record);
13115     var p = f.prototype;
13116     p.fields = new Roo.util.MixedCollection(false, function(field){
13117         return field.name;
13118     });
13119     for(var i = 0, len = o.length; i < len; i++){
13120         p.fields.add(new Roo.data.Field(o[i]));
13121     }
13122     f.getField = function(name){
13123         return p.fields.get(name);  
13124     };
13125     return f;
13126 };
13127
13128 Roo.data.Record.AUTO_ID = 1000;
13129 Roo.data.Record.EDIT = 'edit';
13130 Roo.data.Record.REJECT = 'reject';
13131 Roo.data.Record.COMMIT = 'commit';
13132
13133 Roo.data.Record.prototype = {
13134     /**
13135      * Readonly flag - true if this record has been modified.
13136      * @type Boolean
13137      */
13138     dirty : false,
13139     editing : false,
13140     error: null,
13141     modified: null,
13142
13143     // private
13144     join : function(store){
13145         this.store = store;
13146     },
13147
13148     /**
13149      * Set the named field to the specified value.
13150      * @param {String} name The name of the field to set.
13151      * @param {Object} value The value to set the field to.
13152      */
13153     set : function(name, value){
13154         if(this.data[name] == value){
13155             return;
13156         }
13157         this.dirty = true;
13158         if(!this.modified){
13159             this.modified = {};
13160         }
13161         if(typeof this.modified[name] == 'undefined'){
13162             this.modified[name] = this.data[name];
13163         }
13164         this.data[name] = value;
13165         if(!this.editing && this.store){
13166             this.store.afterEdit(this);
13167         }       
13168     },
13169
13170     /**
13171      * Get the value of the named field.
13172      * @param {String} name The name of the field to get the value of.
13173      * @return {Object} The value of the field.
13174      */
13175     get : function(name){
13176         return this.data[name]; 
13177     },
13178
13179     // private
13180     beginEdit : function(){
13181         this.editing = true;
13182         this.modified = {}; 
13183     },
13184
13185     // private
13186     cancelEdit : function(){
13187         this.editing = false;
13188         delete this.modified;
13189     },
13190
13191     // private
13192     endEdit : function(){
13193         this.editing = false;
13194         if(this.dirty && this.store){
13195             this.store.afterEdit(this);
13196         }
13197     },
13198
13199     /**
13200      * Usually called by the {@link Roo.data.Store} which owns the Record.
13201      * Rejects all changes made to the Record since either creation, or the last commit operation.
13202      * Modified fields are reverted to their original values.
13203      * <p>
13204      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13205      * of reject operations.
13206      */
13207     reject : function(){
13208         var m = this.modified;
13209         for(var n in m){
13210             if(typeof m[n] != "function"){
13211                 this.data[n] = m[n];
13212             }
13213         }
13214         this.dirty = false;
13215         delete this.modified;
13216         this.editing = false;
13217         if(this.store){
13218             this.store.afterReject(this);
13219         }
13220     },
13221
13222     /**
13223      * Usually called by the {@link Roo.data.Store} which owns the Record.
13224      * Commits all changes made to the Record since either creation, or the last commit operation.
13225      * <p>
13226      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13227      * of commit operations.
13228      */
13229     commit : function(){
13230         this.dirty = false;
13231         delete this.modified;
13232         this.editing = false;
13233         if(this.store){
13234             this.store.afterCommit(this);
13235         }
13236     },
13237
13238     // private
13239     hasError : function(){
13240         return this.error != null;
13241     },
13242
13243     // private
13244     clearError : function(){
13245         this.error = null;
13246     },
13247
13248     /**
13249      * Creates a copy of this record.
13250      * @param {String} id (optional) A new record id if you don't want to use this record's id
13251      * @return {Record}
13252      */
13253     copy : function(newId) {
13254         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13255     }
13256 };/*
13257  * Based on:
13258  * Ext JS Library 1.1.1
13259  * Copyright(c) 2006-2007, Ext JS, LLC.
13260  *
13261  * Originally Released Under LGPL - original licence link has changed is not relivant.
13262  *
13263  * Fork - LGPL
13264  * <script type="text/javascript">
13265  */
13266
13267
13268
13269 /**
13270  * @class Roo.data.Store
13271  * @extends Roo.util.Observable
13272  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13273  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13274  * <p>
13275  * 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
13276  * has no knowledge of the format of the data returned by the Proxy.<br>
13277  * <p>
13278  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13279  * instances from the data object. These records are cached and made available through accessor functions.
13280  * @constructor
13281  * Creates a new Store.
13282  * @param {Object} config A config object containing the objects needed for the Store to access data,
13283  * and read the data into Records.
13284  */
13285 Roo.data.Store = function(config){
13286     this.data = new Roo.util.MixedCollection(false);
13287     this.data.getKey = function(o){
13288         return o.id;
13289     };
13290     this.baseParams = {};
13291     // private
13292     this.paramNames = {
13293         "start" : "start",
13294         "limit" : "limit",
13295         "sort" : "sort",
13296         "dir" : "dir",
13297         "multisort" : "_multisort"
13298     };
13299
13300     if(config && config.data){
13301         this.inlineData = config.data;
13302         delete config.data;
13303     }
13304
13305     Roo.apply(this, config);
13306     
13307     if(this.reader){ // reader passed
13308         this.reader = Roo.factory(this.reader, Roo.data);
13309         this.reader.xmodule = this.xmodule || false;
13310         if(!this.recordType){
13311             this.recordType = this.reader.recordType;
13312         }
13313         if(this.reader.onMetaChange){
13314             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13315         }
13316     }
13317
13318     if(this.recordType){
13319         this.fields = this.recordType.prototype.fields;
13320     }
13321     this.modified = [];
13322
13323     this.addEvents({
13324         /**
13325          * @event datachanged
13326          * Fires when the data cache has changed, and a widget which is using this Store
13327          * as a Record cache should refresh its view.
13328          * @param {Store} this
13329          */
13330         datachanged : true,
13331         /**
13332          * @event metachange
13333          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13334          * @param {Store} this
13335          * @param {Object} meta The JSON metadata
13336          */
13337         metachange : true,
13338         /**
13339          * @event add
13340          * Fires when Records have been added to the Store
13341          * @param {Store} this
13342          * @param {Roo.data.Record[]} records The array of Records added
13343          * @param {Number} index The index at which the record(s) were added
13344          */
13345         add : true,
13346         /**
13347          * @event remove
13348          * Fires when a Record has been removed from the Store
13349          * @param {Store} this
13350          * @param {Roo.data.Record} record The Record that was removed
13351          * @param {Number} index The index at which the record was removed
13352          */
13353         remove : true,
13354         /**
13355          * @event update
13356          * Fires when a Record has been updated
13357          * @param {Store} this
13358          * @param {Roo.data.Record} record The Record that was updated
13359          * @param {String} operation The update operation being performed.  Value may be one of:
13360          * <pre><code>
13361  Roo.data.Record.EDIT
13362  Roo.data.Record.REJECT
13363  Roo.data.Record.COMMIT
13364          * </code></pre>
13365          */
13366         update : true,
13367         /**
13368          * @event clear
13369          * Fires when the data cache has been cleared.
13370          * @param {Store} this
13371          */
13372         clear : true,
13373         /**
13374          * @event beforeload
13375          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13376          * the load action will be canceled.
13377          * @param {Store} this
13378          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13379          */
13380         beforeload : true,
13381         /**
13382          * @event beforeloadadd
13383          * Fires after a new set of Records has been loaded.
13384          * @param {Store} this
13385          * @param {Roo.data.Record[]} records The Records that were loaded
13386          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13387          */
13388         beforeloadadd : true,
13389         /**
13390          * @event load
13391          * Fires after a new set of Records has been loaded, before they are added to the store.
13392          * @param {Store} this
13393          * @param {Roo.data.Record[]} records The Records that were loaded
13394          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13395          * @params {Object} return from reader
13396          */
13397         load : true,
13398         /**
13399          * @event loadexception
13400          * Fires if an exception occurs in the Proxy during loading.
13401          * Called with the signature of the Proxy's "loadexception" event.
13402          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13403          * 
13404          * @param {Proxy} 
13405          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13406          * @param {Object} load options 
13407          * @param {Object} jsonData from your request (normally this contains the Exception)
13408          */
13409         loadexception : true
13410     });
13411     
13412     if(this.proxy){
13413         this.proxy = Roo.factory(this.proxy, Roo.data);
13414         this.proxy.xmodule = this.xmodule || false;
13415         this.relayEvents(this.proxy,  ["loadexception"]);
13416     }
13417     this.sortToggle = {};
13418     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13419
13420     Roo.data.Store.superclass.constructor.call(this);
13421
13422     if(this.inlineData){
13423         this.loadData(this.inlineData);
13424         delete this.inlineData;
13425     }
13426 };
13427
13428 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13429      /**
13430     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13431     * without a remote query - used by combo/forms at present.
13432     */
13433     
13434     /**
13435     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13436     */
13437     /**
13438     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13439     */
13440     /**
13441     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13442     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13443     */
13444     /**
13445     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13446     * on any HTTP request
13447     */
13448     /**
13449     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13450     */
13451     /**
13452     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13453     */
13454     multiSort: false,
13455     /**
13456     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13457     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13458     */
13459     remoteSort : false,
13460
13461     /**
13462     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13463      * loaded or when a record is removed. (defaults to false).
13464     */
13465     pruneModifiedRecords : false,
13466
13467     // private
13468     lastOptions : null,
13469
13470     /**
13471      * Add Records to the Store and fires the add event.
13472      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13473      */
13474     add : function(records){
13475         records = [].concat(records);
13476         for(var i = 0, len = records.length; i < len; i++){
13477             records[i].join(this);
13478         }
13479         var index = this.data.length;
13480         this.data.addAll(records);
13481         this.fireEvent("add", this, records, index);
13482     },
13483
13484     /**
13485      * Remove a Record from the Store and fires the remove event.
13486      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13487      */
13488     remove : function(record){
13489         var index = this.data.indexOf(record);
13490         this.data.removeAt(index);
13491  
13492         if(this.pruneModifiedRecords){
13493             this.modified.remove(record);
13494         }
13495         this.fireEvent("remove", this, record, index);
13496     },
13497
13498     /**
13499      * Remove all Records from the Store and fires the clear event.
13500      */
13501     removeAll : function(){
13502         this.data.clear();
13503         if(this.pruneModifiedRecords){
13504             this.modified = [];
13505         }
13506         this.fireEvent("clear", this);
13507     },
13508
13509     /**
13510      * Inserts Records to the Store at the given index and fires the add event.
13511      * @param {Number} index The start index at which to insert the passed Records.
13512      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13513      */
13514     insert : function(index, records){
13515         records = [].concat(records);
13516         for(var i = 0, len = records.length; i < len; i++){
13517             this.data.insert(index, records[i]);
13518             records[i].join(this);
13519         }
13520         this.fireEvent("add", this, records, index);
13521     },
13522
13523     /**
13524      * Get the index within the cache of the passed Record.
13525      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13526      * @return {Number} The index of the passed Record. Returns -1 if not found.
13527      */
13528     indexOf : function(record){
13529         return this.data.indexOf(record);
13530     },
13531
13532     /**
13533      * Get the index within the cache of the Record with the passed id.
13534      * @param {String} id The id of the Record to find.
13535      * @return {Number} The index of the Record. Returns -1 if not found.
13536      */
13537     indexOfId : function(id){
13538         return this.data.indexOfKey(id);
13539     },
13540
13541     /**
13542      * Get the Record with the specified id.
13543      * @param {String} id The id of the Record to find.
13544      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13545      */
13546     getById : function(id){
13547         return this.data.key(id);
13548     },
13549
13550     /**
13551      * Get the Record at the specified index.
13552      * @param {Number} index The index of the Record to find.
13553      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13554      */
13555     getAt : function(index){
13556         return this.data.itemAt(index);
13557     },
13558
13559     /**
13560      * Returns a range of Records between specified indices.
13561      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13562      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13563      * @return {Roo.data.Record[]} An array of Records
13564      */
13565     getRange : function(start, end){
13566         return this.data.getRange(start, end);
13567     },
13568
13569     // private
13570     storeOptions : function(o){
13571         o = Roo.apply({}, o);
13572         delete o.callback;
13573         delete o.scope;
13574         this.lastOptions = o;
13575     },
13576
13577     /**
13578      * Loads the Record cache from the configured Proxy using the configured Reader.
13579      * <p>
13580      * If using remote paging, then the first load call must specify the <em>start</em>
13581      * and <em>limit</em> properties in the options.params property to establish the initial
13582      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13583      * <p>
13584      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13585      * and this call will return before the new data has been loaded. Perform any post-processing
13586      * in a callback function, or in a "load" event handler.</strong>
13587      * <p>
13588      * @param {Object} options An object containing properties which control loading options:<ul>
13589      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13590      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13591      * passed the following arguments:<ul>
13592      * <li>r : Roo.data.Record[]</li>
13593      * <li>options: Options object from the load call</li>
13594      * <li>success: Boolean success indicator</li></ul></li>
13595      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13596      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13597      * </ul>
13598      */
13599     load : function(options){
13600         options = options || {};
13601         if(this.fireEvent("beforeload", this, options) !== false){
13602             this.storeOptions(options);
13603             var p = Roo.apply(options.params || {}, this.baseParams);
13604             // if meta was not loaded from remote source.. try requesting it.
13605             if (!this.reader.metaFromRemote) {
13606                 p._requestMeta = 1;
13607             }
13608             if(this.sortInfo && this.remoteSort){
13609                 var pn = this.paramNames;
13610                 p[pn["sort"]] = this.sortInfo.field;
13611                 p[pn["dir"]] = this.sortInfo.direction;
13612             }
13613             if (this.multiSort) {
13614                 var pn = this.paramNames;
13615                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13616             }
13617             
13618             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13619         }
13620     },
13621
13622     /**
13623      * Reloads the Record cache from the configured Proxy using the configured Reader and
13624      * the options from the last load operation performed.
13625      * @param {Object} options (optional) An object containing properties which may override the options
13626      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13627      * the most recently used options are reused).
13628      */
13629     reload : function(options){
13630         this.load(Roo.applyIf(options||{}, this.lastOptions));
13631     },
13632
13633     // private
13634     // Called as a callback by the Reader during a load operation.
13635     loadRecords : function(o, options, success){
13636         if(!o || success === false){
13637             if(success !== false){
13638                 this.fireEvent("load", this, [], options, o);
13639             }
13640             if(options.callback){
13641                 options.callback.call(options.scope || this, [], options, false);
13642             }
13643             return;
13644         }
13645         // if data returned failure - throw an exception.
13646         if (o.success === false) {
13647             // show a message if no listener is registered.
13648             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13649                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13650             }
13651             // loadmask wil be hooked into this..
13652             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13653             return;
13654         }
13655         var r = o.records, t = o.totalRecords || r.length;
13656         
13657         this.fireEvent("beforeloadadd", this, r, options, o);
13658         
13659         if(!options || options.add !== true){
13660             if(this.pruneModifiedRecords){
13661                 this.modified = [];
13662             }
13663             for(var i = 0, len = r.length; i < len; i++){
13664                 r[i].join(this);
13665             }
13666             if(this.snapshot){
13667                 this.data = this.snapshot;
13668                 delete this.snapshot;
13669             }
13670             this.data.clear();
13671             this.data.addAll(r);
13672             this.totalLength = t;
13673             this.applySort();
13674             this.fireEvent("datachanged", this);
13675         }else{
13676             this.totalLength = Math.max(t, this.data.length+r.length);
13677             this.add(r);
13678         }
13679         
13680         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13681                 
13682             var e = new Roo.data.Record({});
13683
13684             e.set(this.parent.displayField, this.parent.emptyTitle);
13685             e.set(this.parent.valueField, '');
13686
13687             this.insert(0, e);
13688         }
13689             
13690         this.fireEvent("load", this, r, options, o);
13691         if(options.callback){
13692             options.callback.call(options.scope || this, r, options, true);
13693         }
13694     },
13695
13696
13697     /**
13698      * Loads data from a passed data block. A Reader which understands the format of the data
13699      * must have been configured in the constructor.
13700      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13701      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13702      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13703      */
13704     loadData : function(o, append){
13705         var r = this.reader.readRecords(o);
13706         this.loadRecords(r, {add: append}, true);
13707     },
13708     
13709      /**
13710      * using 'cn' the nested child reader read the child array into it's child stores.
13711      * @param {Object} rec The record with a 'children array
13712      */
13713     loadDataFromChildren : function(rec)
13714     {
13715         this.loadData(this.reader.toLoadData(rec));
13716     },
13717     
13718
13719     /**
13720      * Gets the number of cached records.
13721      * <p>
13722      * <em>If using paging, this may not be the total size of the dataset. If the data object
13723      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13724      * the data set size</em>
13725      */
13726     getCount : function(){
13727         return this.data.length || 0;
13728     },
13729
13730     /**
13731      * Gets the total number of records in the dataset as returned by the server.
13732      * <p>
13733      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13734      * the dataset size</em>
13735      */
13736     getTotalCount : function(){
13737         return this.totalLength || 0;
13738     },
13739
13740     /**
13741      * Returns the sort state of the Store as an object with two properties:
13742      * <pre><code>
13743  field {String} The name of the field by which the Records are sorted
13744  direction {String} The sort order, "ASC" or "DESC"
13745      * </code></pre>
13746      */
13747     getSortState : function(){
13748         return this.sortInfo;
13749     },
13750
13751     // private
13752     applySort : function(){
13753         if(this.sortInfo && !this.remoteSort){
13754             var s = this.sortInfo, f = s.field;
13755             var st = this.fields.get(f).sortType;
13756             var fn = function(r1, r2){
13757                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13758                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13759             };
13760             this.data.sort(s.direction, fn);
13761             if(this.snapshot && this.snapshot != this.data){
13762                 this.snapshot.sort(s.direction, fn);
13763             }
13764         }
13765     },
13766
13767     /**
13768      * Sets the default sort column and order to be used by the next load operation.
13769      * @param {String} fieldName The name of the field to sort by.
13770      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13771      */
13772     setDefaultSort : function(field, dir){
13773         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13774     },
13775
13776     /**
13777      * Sort the Records.
13778      * If remote sorting is used, the sort is performed on the server, and the cache is
13779      * reloaded. If local sorting is used, the cache is sorted internally.
13780      * @param {String} fieldName The name of the field to sort by.
13781      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13782      */
13783     sort : function(fieldName, dir){
13784         var f = this.fields.get(fieldName);
13785         if(!dir){
13786             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13787             
13788             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13789                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13790             }else{
13791                 dir = f.sortDir;
13792             }
13793         }
13794         this.sortToggle[f.name] = dir;
13795         this.sortInfo = {field: f.name, direction: dir};
13796         if(!this.remoteSort){
13797             this.applySort();
13798             this.fireEvent("datachanged", this);
13799         }else{
13800             this.load(this.lastOptions);
13801         }
13802     },
13803
13804     /**
13805      * Calls the specified function for each of the Records in the cache.
13806      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13807      * Returning <em>false</em> aborts and exits the iteration.
13808      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13809      */
13810     each : function(fn, scope){
13811         this.data.each(fn, scope);
13812     },
13813
13814     /**
13815      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13816      * (e.g., during paging).
13817      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13818      */
13819     getModifiedRecords : function(){
13820         return this.modified;
13821     },
13822
13823     // private
13824     createFilterFn : function(property, value, anyMatch){
13825         if(!value.exec){ // not a regex
13826             value = String(value);
13827             if(value.length == 0){
13828                 return false;
13829             }
13830             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13831         }
13832         return function(r){
13833             return value.test(r.data[property]);
13834         };
13835     },
13836
13837     /**
13838      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13839      * @param {String} property A field on your records
13840      * @param {Number} start The record index to start at (defaults to 0)
13841      * @param {Number} end The last record index to include (defaults to length - 1)
13842      * @return {Number} The sum
13843      */
13844     sum : function(property, start, end){
13845         var rs = this.data.items, v = 0;
13846         start = start || 0;
13847         end = (end || end === 0) ? end : rs.length-1;
13848
13849         for(var i = start; i <= end; i++){
13850             v += (rs[i].data[property] || 0);
13851         }
13852         return v;
13853     },
13854
13855     /**
13856      * Filter the records by a specified property.
13857      * @param {String} field A field on your records
13858      * @param {String/RegExp} value Either a string that the field
13859      * should start with or a RegExp to test against the field
13860      * @param {Boolean} anyMatch True to match any part not just the beginning
13861      */
13862     filter : function(property, value, anyMatch){
13863         var fn = this.createFilterFn(property, value, anyMatch);
13864         return fn ? this.filterBy(fn) : this.clearFilter();
13865     },
13866
13867     /**
13868      * Filter by a function. The specified function will be called with each
13869      * record in this data source. If the function returns true the record is included,
13870      * otherwise it is filtered.
13871      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13872      * @param {Object} scope (optional) The scope of the function (defaults to this)
13873      */
13874     filterBy : function(fn, scope){
13875         this.snapshot = this.snapshot || this.data;
13876         this.data = this.queryBy(fn, scope||this);
13877         this.fireEvent("datachanged", this);
13878     },
13879
13880     /**
13881      * Query the records by a specified property.
13882      * @param {String} field A field on your records
13883      * @param {String/RegExp} value Either a string that the field
13884      * should start with or a RegExp to test against the field
13885      * @param {Boolean} anyMatch True to match any part not just the beginning
13886      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13887      */
13888     query : function(property, value, anyMatch){
13889         var fn = this.createFilterFn(property, value, anyMatch);
13890         return fn ? this.queryBy(fn) : this.data.clone();
13891     },
13892
13893     /**
13894      * Query by a function. The specified function will be called with each
13895      * record in this data source. If the function returns true the record is included
13896      * in the results.
13897      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13898      * @param {Object} scope (optional) The scope of the function (defaults to this)
13899       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13900      **/
13901     queryBy : function(fn, scope){
13902         var data = this.snapshot || this.data;
13903         return data.filterBy(fn, scope||this);
13904     },
13905
13906     /**
13907      * Collects unique values for a particular dataIndex from this store.
13908      * @param {String} dataIndex The property to collect
13909      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13910      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13911      * @return {Array} An array of the unique values
13912      **/
13913     collect : function(dataIndex, allowNull, bypassFilter){
13914         var d = (bypassFilter === true && this.snapshot) ?
13915                 this.snapshot.items : this.data.items;
13916         var v, sv, r = [], l = {};
13917         for(var i = 0, len = d.length; i < len; i++){
13918             v = d[i].data[dataIndex];
13919             sv = String(v);
13920             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13921                 l[sv] = true;
13922                 r[r.length] = v;
13923             }
13924         }
13925         return r;
13926     },
13927
13928     /**
13929      * Revert to a view of the Record cache with no filtering applied.
13930      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13931      */
13932     clearFilter : function(suppressEvent){
13933         if(this.snapshot && this.snapshot != this.data){
13934             this.data = this.snapshot;
13935             delete this.snapshot;
13936             if(suppressEvent !== true){
13937                 this.fireEvent("datachanged", this);
13938             }
13939         }
13940     },
13941
13942     // private
13943     afterEdit : function(record){
13944         if(this.modified.indexOf(record) == -1){
13945             this.modified.push(record);
13946         }
13947         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13948     },
13949     
13950     // private
13951     afterReject : function(record){
13952         this.modified.remove(record);
13953         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13954     },
13955
13956     // private
13957     afterCommit : function(record){
13958         this.modified.remove(record);
13959         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13960     },
13961
13962     /**
13963      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13964      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13965      */
13966     commitChanges : function(){
13967         var m = this.modified.slice(0);
13968         this.modified = [];
13969         for(var i = 0, len = m.length; i < len; i++){
13970             m[i].commit();
13971         }
13972     },
13973
13974     /**
13975      * Cancel outstanding changes on all changed records.
13976      */
13977     rejectChanges : function(){
13978         var m = this.modified.slice(0);
13979         this.modified = [];
13980         for(var i = 0, len = m.length; i < len; i++){
13981             m[i].reject();
13982         }
13983     },
13984
13985     onMetaChange : function(meta, rtype, o){
13986         this.recordType = rtype;
13987         this.fields = rtype.prototype.fields;
13988         delete this.snapshot;
13989         this.sortInfo = meta.sortInfo || this.sortInfo;
13990         this.modified = [];
13991         this.fireEvent('metachange', this, this.reader.meta);
13992     },
13993     
13994     moveIndex : function(data, type)
13995     {
13996         var index = this.indexOf(data);
13997         
13998         var newIndex = index + type;
13999         
14000         this.remove(data);
14001         
14002         this.insert(newIndex, data);
14003         
14004     }
14005 });/*
14006  * Based on:
14007  * Ext JS Library 1.1.1
14008  * Copyright(c) 2006-2007, Ext JS, LLC.
14009  *
14010  * Originally Released Under LGPL - original licence link has changed is not relivant.
14011  *
14012  * Fork - LGPL
14013  * <script type="text/javascript">
14014  */
14015
14016 /**
14017  * @class Roo.data.SimpleStore
14018  * @extends Roo.data.Store
14019  * Small helper class to make creating Stores from Array data easier.
14020  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14021  * @cfg {Array} fields An array of field definition objects, or field name strings.
14022  * @cfg {Object} an existing reader (eg. copied from another store)
14023  * @cfg {Array} data The multi-dimensional array of data
14024  * @constructor
14025  * @param {Object} config
14026  */
14027 Roo.data.SimpleStore = function(config)
14028 {
14029     Roo.data.SimpleStore.superclass.constructor.call(this, {
14030         isLocal : true,
14031         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14032                 id: config.id
14033             },
14034             Roo.data.Record.create(config.fields)
14035         ),
14036         proxy : new Roo.data.MemoryProxy(config.data)
14037     });
14038     this.load();
14039 };
14040 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14041  * Based on:
14042  * Ext JS Library 1.1.1
14043  * Copyright(c) 2006-2007, Ext JS, LLC.
14044  *
14045  * Originally Released Under LGPL - original licence link has changed is not relivant.
14046  *
14047  * Fork - LGPL
14048  * <script type="text/javascript">
14049  */
14050
14051 /**
14052 /**
14053  * @extends Roo.data.Store
14054  * @class Roo.data.JsonStore
14055  * Small helper class to make creating Stores for JSON data easier. <br/>
14056 <pre><code>
14057 var store = new Roo.data.JsonStore({
14058     url: 'get-images.php',
14059     root: 'images',
14060     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14061 });
14062 </code></pre>
14063  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14064  * JsonReader and HttpProxy (unless inline data is provided).</b>
14065  * @cfg {Array} fields An array of field definition objects, or field name strings.
14066  * @constructor
14067  * @param {Object} config
14068  */
14069 Roo.data.JsonStore = function(c){
14070     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14071         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14072         reader: new Roo.data.JsonReader(c, c.fields)
14073     }));
14074 };
14075 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14076  * Based on:
14077  * Ext JS Library 1.1.1
14078  * Copyright(c) 2006-2007, Ext JS, LLC.
14079  *
14080  * Originally Released Under LGPL - original licence link has changed is not relivant.
14081  *
14082  * Fork - LGPL
14083  * <script type="text/javascript">
14084  */
14085
14086  
14087 Roo.data.Field = function(config){
14088     if(typeof config == "string"){
14089         config = {name: config};
14090     }
14091     Roo.apply(this, config);
14092     
14093     if(!this.type){
14094         this.type = "auto";
14095     }
14096     
14097     var st = Roo.data.SortTypes;
14098     // named sortTypes are supported, here we look them up
14099     if(typeof this.sortType == "string"){
14100         this.sortType = st[this.sortType];
14101     }
14102     
14103     // set default sortType for strings and dates
14104     if(!this.sortType){
14105         switch(this.type){
14106             case "string":
14107                 this.sortType = st.asUCString;
14108                 break;
14109             case "date":
14110                 this.sortType = st.asDate;
14111                 break;
14112             default:
14113                 this.sortType = st.none;
14114         }
14115     }
14116
14117     // define once
14118     var stripRe = /[\$,%]/g;
14119
14120     // prebuilt conversion function for this field, instead of
14121     // switching every time we're reading a value
14122     if(!this.convert){
14123         var cv, dateFormat = this.dateFormat;
14124         switch(this.type){
14125             case "":
14126             case "auto":
14127             case undefined:
14128                 cv = function(v){ return v; };
14129                 break;
14130             case "string":
14131                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14132                 break;
14133             case "int":
14134                 cv = function(v){
14135                     return v !== undefined && v !== null && v !== '' ?
14136                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14137                     };
14138                 break;
14139             case "float":
14140                 cv = function(v){
14141                     return v !== undefined && v !== null && v !== '' ?
14142                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14143                     };
14144                 break;
14145             case "bool":
14146             case "boolean":
14147                 cv = function(v){ return v === true || v === "true" || v == 1; };
14148                 break;
14149             case "date":
14150                 cv = function(v){
14151                     if(!v){
14152                         return '';
14153                     }
14154                     if(v instanceof Date){
14155                         return v;
14156                     }
14157                     if(dateFormat){
14158                         if(dateFormat == "timestamp"){
14159                             return new Date(v*1000);
14160                         }
14161                         return Date.parseDate(v, dateFormat);
14162                     }
14163                     var parsed = Date.parse(v);
14164                     return parsed ? new Date(parsed) : null;
14165                 };
14166              break;
14167             
14168         }
14169         this.convert = cv;
14170     }
14171 };
14172
14173 Roo.data.Field.prototype = {
14174     dateFormat: null,
14175     defaultValue: "",
14176     mapping: null,
14177     sortType : null,
14178     sortDir : "ASC"
14179 };/*
14180  * Based on:
14181  * Ext JS Library 1.1.1
14182  * Copyright(c) 2006-2007, Ext JS, LLC.
14183  *
14184  * Originally Released Under LGPL - original licence link has changed is not relivant.
14185  *
14186  * Fork - LGPL
14187  * <script type="text/javascript">
14188  */
14189  
14190 // Base class for reading structured data from a data source.  This class is intended to be
14191 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14192
14193 /**
14194  * @class Roo.data.DataReader
14195  * Base class for reading structured data from a data source.  This class is intended to be
14196  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14197  */
14198
14199 Roo.data.DataReader = function(meta, recordType){
14200     
14201     this.meta = meta;
14202     
14203     this.recordType = recordType instanceof Array ? 
14204         Roo.data.Record.create(recordType) : recordType;
14205 };
14206
14207 Roo.data.DataReader.prototype = {
14208     
14209     
14210     readerType : 'Data',
14211      /**
14212      * Create an empty record
14213      * @param {Object} data (optional) - overlay some values
14214      * @return {Roo.data.Record} record created.
14215      */
14216     newRow :  function(d) {
14217         var da =  {};
14218         this.recordType.prototype.fields.each(function(c) {
14219             switch( c.type) {
14220                 case 'int' : da[c.name] = 0; break;
14221                 case 'date' : da[c.name] = new Date(); break;
14222                 case 'float' : da[c.name] = 0.0; break;
14223                 case 'boolean' : da[c.name] = false; break;
14224                 default : da[c.name] = ""; break;
14225             }
14226             
14227         });
14228         return new this.recordType(Roo.apply(da, d));
14229     }
14230     
14231     
14232 };/*
14233  * Based on:
14234  * Ext JS Library 1.1.1
14235  * Copyright(c) 2006-2007, Ext JS, LLC.
14236  *
14237  * Originally Released Under LGPL - original licence link has changed is not relivant.
14238  *
14239  * Fork - LGPL
14240  * <script type="text/javascript">
14241  */
14242
14243 /**
14244  * @class Roo.data.DataProxy
14245  * @extends Roo.data.Observable
14246  * This class is an abstract base class for implementations which provide retrieval of
14247  * unformatted data objects.<br>
14248  * <p>
14249  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14250  * (of the appropriate type which knows how to parse the data object) to provide a block of
14251  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14252  * <p>
14253  * Custom implementations must implement the load method as described in
14254  * {@link Roo.data.HttpProxy#load}.
14255  */
14256 Roo.data.DataProxy = function(){
14257     this.addEvents({
14258         /**
14259          * @event beforeload
14260          * Fires before a network request is made to retrieve a data object.
14261          * @param {Object} This DataProxy object.
14262          * @param {Object} params The params parameter to the load function.
14263          */
14264         beforeload : true,
14265         /**
14266          * @event load
14267          * Fires before the load method's callback is called.
14268          * @param {Object} This DataProxy object.
14269          * @param {Object} o The data object.
14270          * @param {Object} arg The callback argument object passed to the load function.
14271          */
14272         load : true,
14273         /**
14274          * @event loadexception
14275          * Fires if an Exception occurs during data retrieval.
14276          * @param {Object} This DataProxy object.
14277          * @param {Object} o The data object.
14278          * @param {Object} arg The callback argument object passed to the load function.
14279          * @param {Object} e The Exception.
14280          */
14281         loadexception : true
14282     });
14283     Roo.data.DataProxy.superclass.constructor.call(this);
14284 };
14285
14286 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14287
14288     /**
14289      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14290      */
14291 /*
14292  * Based on:
14293  * Ext JS Library 1.1.1
14294  * Copyright(c) 2006-2007, Ext JS, LLC.
14295  *
14296  * Originally Released Under LGPL - original licence link has changed is not relivant.
14297  *
14298  * Fork - LGPL
14299  * <script type="text/javascript">
14300  */
14301 /**
14302  * @class Roo.data.MemoryProxy
14303  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14304  * to the Reader when its load method is called.
14305  * @constructor
14306  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14307  */
14308 Roo.data.MemoryProxy = function(data){
14309     if (data.data) {
14310         data = data.data;
14311     }
14312     Roo.data.MemoryProxy.superclass.constructor.call(this);
14313     this.data = data;
14314 };
14315
14316 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14317     
14318     /**
14319      * Load data from the requested source (in this case an in-memory
14320      * data object passed to the constructor), read the data object into
14321      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14322      * process that block using the passed callback.
14323      * @param {Object} params This parameter is not used by the MemoryProxy class.
14324      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14325      * object into a block of Roo.data.Records.
14326      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14327      * The function must be passed <ul>
14328      * <li>The Record block object</li>
14329      * <li>The "arg" argument from the load function</li>
14330      * <li>A boolean success indicator</li>
14331      * </ul>
14332      * @param {Object} scope The scope in which to call the callback
14333      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14334      */
14335     load : function(params, reader, callback, scope, arg){
14336         params = params || {};
14337         var result;
14338         try {
14339             result = reader.readRecords(params.data ? params.data :this.data);
14340         }catch(e){
14341             this.fireEvent("loadexception", this, arg, null, e);
14342             callback.call(scope, null, arg, false);
14343             return;
14344         }
14345         callback.call(scope, result, arg, true);
14346     },
14347     
14348     // private
14349     update : function(params, records){
14350         
14351     }
14352 });/*
14353  * Based on:
14354  * Ext JS Library 1.1.1
14355  * Copyright(c) 2006-2007, Ext JS, LLC.
14356  *
14357  * Originally Released Under LGPL - original licence link has changed is not relivant.
14358  *
14359  * Fork - LGPL
14360  * <script type="text/javascript">
14361  */
14362 /**
14363  * @class Roo.data.HttpProxy
14364  * @extends Roo.data.DataProxy
14365  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14366  * configured to reference a certain URL.<br><br>
14367  * <p>
14368  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14369  * from which the running page was served.<br><br>
14370  * <p>
14371  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14372  * <p>
14373  * Be aware that to enable the browser to parse an XML document, the server must set
14374  * the Content-Type header in the HTTP response to "text/xml".
14375  * @constructor
14376  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14377  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14378  * will be used to make the request.
14379  */
14380 Roo.data.HttpProxy = function(conn){
14381     Roo.data.HttpProxy.superclass.constructor.call(this);
14382     // is conn a conn config or a real conn?
14383     this.conn = conn;
14384     this.useAjax = !conn || !conn.events;
14385   
14386 };
14387
14388 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14389     // thse are take from connection...
14390     
14391     /**
14392      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14393      */
14394     /**
14395      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14396      * extra parameters to each request made by this object. (defaults to undefined)
14397      */
14398     /**
14399      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14400      *  to each request made by this object. (defaults to undefined)
14401      */
14402     /**
14403      * @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)
14404      */
14405     /**
14406      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14407      */
14408      /**
14409      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14410      * @type Boolean
14411      */
14412   
14413
14414     /**
14415      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14416      * @type Boolean
14417      */
14418     /**
14419      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14420      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14421      * a finer-grained basis than the DataProxy events.
14422      */
14423     getConnection : function(){
14424         return this.useAjax ? Roo.Ajax : this.conn;
14425     },
14426
14427     /**
14428      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14429      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14430      * process that block using the passed callback.
14431      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14432      * for the request to the remote server.
14433      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14434      * object into a block of Roo.data.Records.
14435      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14436      * The function must be passed <ul>
14437      * <li>The Record block object</li>
14438      * <li>The "arg" argument from the load function</li>
14439      * <li>A boolean success indicator</li>
14440      * </ul>
14441      * @param {Object} scope The scope in which to call the callback
14442      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14443      */
14444     load : function(params, reader, callback, scope, arg){
14445         if(this.fireEvent("beforeload", this, params) !== false){
14446             var  o = {
14447                 params : params || {},
14448                 request: {
14449                     callback : callback,
14450                     scope : scope,
14451                     arg : arg
14452                 },
14453                 reader: reader,
14454                 callback : this.loadResponse,
14455                 scope: this
14456             };
14457             if(this.useAjax){
14458                 Roo.applyIf(o, this.conn);
14459                 if(this.activeRequest){
14460                     Roo.Ajax.abort(this.activeRequest);
14461                 }
14462                 this.activeRequest = Roo.Ajax.request(o);
14463             }else{
14464                 this.conn.request(o);
14465             }
14466         }else{
14467             callback.call(scope||this, null, arg, false);
14468         }
14469     },
14470
14471     // private
14472     loadResponse : function(o, success, response){
14473         delete this.activeRequest;
14474         if(!success){
14475             this.fireEvent("loadexception", this, o, response);
14476             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14477             return;
14478         }
14479         var result;
14480         try {
14481             result = o.reader.read(response);
14482         }catch(e){
14483             this.fireEvent("loadexception", this, o, response, e);
14484             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14485             return;
14486         }
14487         
14488         this.fireEvent("load", this, o, o.request.arg);
14489         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14490     },
14491
14492     // private
14493     update : function(dataSet){
14494
14495     },
14496
14497     // private
14498     updateResponse : function(dataSet){
14499
14500     }
14501 });/*
14502  * Based on:
14503  * Ext JS Library 1.1.1
14504  * Copyright(c) 2006-2007, Ext JS, LLC.
14505  *
14506  * Originally Released Under LGPL - original licence link has changed is not relivant.
14507  *
14508  * Fork - LGPL
14509  * <script type="text/javascript">
14510  */
14511
14512 /**
14513  * @class Roo.data.ScriptTagProxy
14514  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14515  * other than the originating domain of the running page.<br><br>
14516  * <p>
14517  * <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
14518  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14519  * <p>
14520  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14521  * source code that is used as the source inside a &lt;script> tag.<br><br>
14522  * <p>
14523  * In order for the browser to process the returned data, the server must wrap the data object
14524  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14525  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14526  * depending on whether the callback name was passed:
14527  * <p>
14528  * <pre><code>
14529 boolean scriptTag = false;
14530 String cb = request.getParameter("callback");
14531 if (cb != null) {
14532     scriptTag = true;
14533     response.setContentType("text/javascript");
14534 } else {
14535     response.setContentType("application/x-json");
14536 }
14537 Writer out = response.getWriter();
14538 if (scriptTag) {
14539     out.write(cb + "(");
14540 }
14541 out.print(dataBlock.toJsonString());
14542 if (scriptTag) {
14543     out.write(");");
14544 }
14545 </pre></code>
14546  *
14547  * @constructor
14548  * @param {Object} config A configuration object.
14549  */
14550 Roo.data.ScriptTagProxy = function(config){
14551     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14552     Roo.apply(this, config);
14553     this.head = document.getElementsByTagName("head")[0];
14554 };
14555
14556 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14557
14558 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14559     /**
14560      * @cfg {String} url The URL from which to request the data object.
14561      */
14562     /**
14563      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14564      */
14565     timeout : 30000,
14566     /**
14567      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14568      * the server the name of the callback function set up by the load call to process the returned data object.
14569      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14570      * javascript output which calls this named function passing the data object as its only parameter.
14571      */
14572     callbackParam : "callback",
14573     /**
14574      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14575      * name to the request.
14576      */
14577     nocache : true,
14578
14579     /**
14580      * Load data from the configured URL, read the data object into
14581      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14582      * process that block using the passed callback.
14583      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14584      * for the request to the remote server.
14585      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14586      * object into a block of Roo.data.Records.
14587      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14588      * The function must be passed <ul>
14589      * <li>The Record block object</li>
14590      * <li>The "arg" argument from the load function</li>
14591      * <li>A boolean success indicator</li>
14592      * </ul>
14593      * @param {Object} scope The scope in which to call the callback
14594      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14595      */
14596     load : function(params, reader, callback, scope, arg){
14597         if(this.fireEvent("beforeload", this, params) !== false){
14598
14599             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14600
14601             var url = this.url;
14602             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14603             if(this.nocache){
14604                 url += "&_dc=" + (new Date().getTime());
14605             }
14606             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14607             var trans = {
14608                 id : transId,
14609                 cb : "stcCallback"+transId,
14610                 scriptId : "stcScript"+transId,
14611                 params : params,
14612                 arg : arg,
14613                 url : url,
14614                 callback : callback,
14615                 scope : scope,
14616                 reader : reader
14617             };
14618             var conn = this;
14619
14620             window[trans.cb] = function(o){
14621                 conn.handleResponse(o, trans);
14622             };
14623
14624             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14625
14626             if(this.autoAbort !== false){
14627                 this.abort();
14628             }
14629
14630             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14631
14632             var script = document.createElement("script");
14633             script.setAttribute("src", url);
14634             script.setAttribute("type", "text/javascript");
14635             script.setAttribute("id", trans.scriptId);
14636             this.head.appendChild(script);
14637
14638             this.trans = trans;
14639         }else{
14640             callback.call(scope||this, null, arg, false);
14641         }
14642     },
14643
14644     // private
14645     isLoading : function(){
14646         return this.trans ? true : false;
14647     },
14648
14649     /**
14650      * Abort the current server request.
14651      */
14652     abort : function(){
14653         if(this.isLoading()){
14654             this.destroyTrans(this.trans);
14655         }
14656     },
14657
14658     // private
14659     destroyTrans : function(trans, isLoaded){
14660         this.head.removeChild(document.getElementById(trans.scriptId));
14661         clearTimeout(trans.timeoutId);
14662         if(isLoaded){
14663             window[trans.cb] = undefined;
14664             try{
14665                 delete window[trans.cb];
14666             }catch(e){}
14667         }else{
14668             // if hasn't been loaded, wait for load to remove it to prevent script error
14669             window[trans.cb] = function(){
14670                 window[trans.cb] = undefined;
14671                 try{
14672                     delete window[trans.cb];
14673                 }catch(e){}
14674             };
14675         }
14676     },
14677
14678     // private
14679     handleResponse : function(o, trans){
14680         this.trans = false;
14681         this.destroyTrans(trans, true);
14682         var result;
14683         try {
14684             result = trans.reader.readRecords(o);
14685         }catch(e){
14686             this.fireEvent("loadexception", this, o, trans.arg, e);
14687             trans.callback.call(trans.scope||window, null, trans.arg, false);
14688             return;
14689         }
14690         this.fireEvent("load", this, o, trans.arg);
14691         trans.callback.call(trans.scope||window, result, trans.arg, true);
14692     },
14693
14694     // private
14695     handleFailure : function(trans){
14696         this.trans = false;
14697         this.destroyTrans(trans, false);
14698         this.fireEvent("loadexception", this, null, trans.arg);
14699         trans.callback.call(trans.scope||window, null, trans.arg, false);
14700     }
14701 });/*
14702  * Based on:
14703  * Ext JS Library 1.1.1
14704  * Copyright(c) 2006-2007, Ext JS, LLC.
14705  *
14706  * Originally Released Under LGPL - original licence link has changed is not relivant.
14707  *
14708  * Fork - LGPL
14709  * <script type="text/javascript">
14710  */
14711
14712 /**
14713  * @class Roo.data.JsonReader
14714  * @extends Roo.data.DataReader
14715  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14716  * based on mappings in a provided Roo.data.Record constructor.
14717  * 
14718  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14719  * in the reply previously. 
14720  * 
14721  * <p>
14722  * Example code:
14723  * <pre><code>
14724 var RecordDef = Roo.data.Record.create([
14725     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14726     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14727 ]);
14728 var myReader = new Roo.data.JsonReader({
14729     totalProperty: "results",    // The property which contains the total dataset size (optional)
14730     root: "rows",                // The property which contains an Array of row objects
14731     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14732 }, RecordDef);
14733 </code></pre>
14734  * <p>
14735  * This would consume a JSON file like this:
14736  * <pre><code>
14737 { 'results': 2, 'rows': [
14738     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14739     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14740 }
14741 </code></pre>
14742  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14743  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14744  * paged from the remote server.
14745  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14746  * @cfg {String} root name of the property which contains the Array of row objects.
14747  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14748  * @cfg {Array} fields Array of field definition objects
14749  * @constructor
14750  * Create a new JsonReader
14751  * @param {Object} meta Metadata configuration options
14752  * @param {Object} recordType Either an Array of field definition objects,
14753  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14754  */
14755 Roo.data.JsonReader = function(meta, recordType){
14756     
14757     meta = meta || {};
14758     // set some defaults:
14759     Roo.applyIf(meta, {
14760         totalProperty: 'total',
14761         successProperty : 'success',
14762         root : 'data',
14763         id : 'id'
14764     });
14765     
14766     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14767 };
14768 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14769     
14770     readerType : 'Json',
14771     
14772     /**
14773      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14774      * Used by Store query builder to append _requestMeta to params.
14775      * 
14776      */
14777     metaFromRemote : false,
14778     /**
14779      * This method is only used by a DataProxy which has retrieved data from a remote server.
14780      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14781      * @return {Object} data A data block which is used by an Roo.data.Store object as
14782      * a cache of Roo.data.Records.
14783      */
14784     read : function(response){
14785         var json = response.responseText;
14786        
14787         var o = /* eval:var:o */ eval("("+json+")");
14788         if(!o) {
14789             throw {message: "JsonReader.read: Json object not found"};
14790         }
14791         
14792         if(o.metaData){
14793             
14794             delete this.ef;
14795             this.metaFromRemote = true;
14796             this.meta = o.metaData;
14797             this.recordType = Roo.data.Record.create(o.metaData.fields);
14798             this.onMetaChange(this.meta, this.recordType, o);
14799         }
14800         return this.readRecords(o);
14801     },
14802
14803     // private function a store will implement
14804     onMetaChange : function(meta, recordType, o){
14805
14806     },
14807
14808     /**
14809          * @ignore
14810          */
14811     simpleAccess: function(obj, subsc) {
14812         return obj[subsc];
14813     },
14814
14815         /**
14816          * @ignore
14817          */
14818     getJsonAccessor: function(){
14819         var re = /[\[\.]/;
14820         return function(expr) {
14821             try {
14822                 return(re.test(expr))
14823                     ? new Function("obj", "return obj." + expr)
14824                     : function(obj){
14825                         return obj[expr];
14826                     };
14827             } catch(e){}
14828             return Roo.emptyFn;
14829         };
14830     }(),
14831
14832     /**
14833      * Create a data block containing Roo.data.Records from an XML document.
14834      * @param {Object} o An object which contains an Array of row objects in the property specified
14835      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14836      * which contains the total size of the dataset.
14837      * @return {Object} data A data block which is used by an Roo.data.Store object as
14838      * a cache of Roo.data.Records.
14839      */
14840     readRecords : function(o){
14841         /**
14842          * After any data loads, the raw JSON data is available for further custom processing.
14843          * @type Object
14844          */
14845         this.o = o;
14846         var s = this.meta, Record = this.recordType,
14847             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14848
14849 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14850         if (!this.ef) {
14851             if(s.totalProperty) {
14852                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14853                 }
14854                 if(s.successProperty) {
14855                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14856                 }
14857                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14858                 if (s.id) {
14859                         var g = this.getJsonAccessor(s.id);
14860                         this.getId = function(rec) {
14861                                 var r = g(rec);  
14862                                 return (r === undefined || r === "") ? null : r;
14863                         };
14864                 } else {
14865                         this.getId = function(){return null;};
14866                 }
14867             this.ef = [];
14868             for(var jj = 0; jj < fl; jj++){
14869                 f = fi[jj];
14870                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14871                 this.ef[jj] = this.getJsonAccessor(map);
14872             }
14873         }
14874
14875         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14876         if(s.totalProperty){
14877             var vt = parseInt(this.getTotal(o), 10);
14878             if(!isNaN(vt)){
14879                 totalRecords = vt;
14880             }
14881         }
14882         if(s.successProperty){
14883             var vs = this.getSuccess(o);
14884             if(vs === false || vs === 'false'){
14885                 success = false;
14886             }
14887         }
14888         var records = [];
14889         for(var i = 0; i < c; i++){
14890                 var n = root[i];
14891             var values = {};
14892             var id = this.getId(n);
14893             for(var j = 0; j < fl; j++){
14894                 f = fi[j];
14895             var v = this.ef[j](n);
14896             if (!f.convert) {
14897                 Roo.log('missing convert for ' + f.name);
14898                 Roo.log(f);
14899                 continue;
14900             }
14901             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14902             }
14903             var record = new Record(values, id);
14904             record.json = n;
14905             records[i] = record;
14906         }
14907         return {
14908             raw : o,
14909             success : success,
14910             records : records,
14911             totalRecords : totalRecords
14912         };
14913     },
14914     // used when loading children.. @see loadDataFromChildren
14915     toLoadData: function(rec)
14916     {
14917         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14918         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14919         return { data : data, total : data.length };
14920         
14921     }
14922 });/*
14923  * Based on:
14924  * Ext JS Library 1.1.1
14925  * Copyright(c) 2006-2007, Ext JS, LLC.
14926  *
14927  * Originally Released Under LGPL - original licence link has changed is not relivant.
14928  *
14929  * Fork - LGPL
14930  * <script type="text/javascript">
14931  */
14932
14933 /**
14934  * @class Roo.data.ArrayReader
14935  * @extends Roo.data.DataReader
14936  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14937  * Each element of that Array represents a row of data fields. The
14938  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14939  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14940  * <p>
14941  * Example code:.
14942  * <pre><code>
14943 var RecordDef = Roo.data.Record.create([
14944     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14945     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14946 ]);
14947 var myReader = new Roo.data.ArrayReader({
14948     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14949 }, RecordDef);
14950 </code></pre>
14951  * <p>
14952  * This would consume an Array like this:
14953  * <pre><code>
14954 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14955   </code></pre>
14956  
14957  * @constructor
14958  * Create a new JsonReader
14959  * @param {Object} meta Metadata configuration options.
14960  * @param {Object|Array} recordType Either an Array of field definition objects
14961  * 
14962  * @cfg {Array} fields Array of field definition objects
14963  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14964  * as specified to {@link Roo.data.Record#create},
14965  * or an {@link Roo.data.Record} object
14966  *
14967  * 
14968  * created using {@link Roo.data.Record#create}.
14969  */
14970 Roo.data.ArrayReader = function(meta, recordType)
14971 {    
14972     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14973 };
14974
14975 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14976     
14977       /**
14978      * Create a data block containing Roo.data.Records from an XML document.
14979      * @param {Object} o An Array of row objects which represents the dataset.
14980      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14981      * a cache of Roo.data.Records.
14982      */
14983     readRecords : function(o)
14984     {
14985         var sid = this.meta ? this.meta.id : null;
14986         var recordType = this.recordType, fields = recordType.prototype.fields;
14987         var records = [];
14988         var root = o;
14989         for(var i = 0; i < root.length; i++){
14990                 var n = root[i];
14991             var values = {};
14992             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14993             for(var j = 0, jlen = fields.length; j < jlen; j++){
14994                 var f = fields.items[j];
14995                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14996                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14997                 v = f.convert(v);
14998                 values[f.name] = v;
14999             }
15000             var record = new recordType(values, id);
15001             record.json = n;
15002             records[records.length] = record;
15003         }
15004         return {
15005             records : records,
15006             totalRecords : records.length
15007         };
15008     },
15009     // used when loading children.. @see loadDataFromChildren
15010     toLoadData: function(rec)
15011     {
15012         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15013         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15014         
15015     }
15016     
15017     
15018 });/*
15019  * - LGPL
15020  * * 
15021  */
15022
15023 /**
15024  * @class Roo.bootstrap.ComboBox
15025  * @extends Roo.bootstrap.TriggerField
15026  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15027  * @cfg {Boolean} append (true|false) default false
15028  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15029  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15030  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15031  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15032  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15033  * @cfg {Boolean} animate default true
15034  * @cfg {Boolean} emptyResultText only for touch device
15035  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15036  * @cfg {String} emptyTitle default ''
15037  * @cfg {Number} width fixed with? experimental
15038  * @constructor
15039  * Create a new ComboBox.
15040  * @param {Object} config Configuration options
15041  */
15042 Roo.bootstrap.ComboBox = function(config){
15043     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15044     this.addEvents({
15045         /**
15046          * @event expand
15047          * Fires when the dropdown list is expanded
15048         * @param {Roo.bootstrap.ComboBox} combo This combo box
15049         */
15050         'expand' : true,
15051         /**
15052          * @event collapse
15053          * Fires when the dropdown list is collapsed
15054         * @param {Roo.bootstrap.ComboBox} combo This combo box
15055         */
15056         'collapse' : true,
15057         /**
15058          * @event beforeselect
15059          * Fires before a list item is selected. Return false to cancel the selection.
15060         * @param {Roo.bootstrap.ComboBox} combo This combo box
15061         * @param {Roo.data.Record} record The data record returned from the underlying store
15062         * @param {Number} index The index of the selected item in the dropdown list
15063         */
15064         'beforeselect' : true,
15065         /**
15066          * @event select
15067          * Fires when a list item is selected
15068         * @param {Roo.bootstrap.ComboBox} combo This combo box
15069         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15070         * @param {Number} index The index of the selected item in the dropdown list
15071         */
15072         'select' : true,
15073         /**
15074          * @event beforequery
15075          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15076          * The event object passed has these properties:
15077         * @param {Roo.bootstrap.ComboBox} combo This combo box
15078         * @param {String} query The query
15079         * @param {Boolean} forceAll true to force "all" query
15080         * @param {Boolean} cancel true to cancel the query
15081         * @param {Object} e The query event object
15082         */
15083         'beforequery': true,
15084          /**
15085          * @event add
15086          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15087         * @param {Roo.bootstrap.ComboBox} combo This combo box
15088         */
15089         'add' : true,
15090         /**
15091          * @event edit
15092          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15093         * @param {Roo.bootstrap.ComboBox} combo This combo box
15094         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15095         */
15096         'edit' : true,
15097         /**
15098          * @event remove
15099          * Fires when the remove value from the combobox array
15100         * @param {Roo.bootstrap.ComboBox} combo This combo box
15101         */
15102         'remove' : true,
15103         /**
15104          * @event afterremove
15105          * Fires when the remove value from the combobox array
15106         * @param {Roo.bootstrap.ComboBox} combo This combo box
15107         */
15108         'afterremove' : true,
15109         /**
15110          * @event specialfilter
15111          * Fires when specialfilter
15112             * @param {Roo.bootstrap.ComboBox} combo This combo box
15113             */
15114         'specialfilter' : true,
15115         /**
15116          * @event tick
15117          * Fires when tick the element
15118             * @param {Roo.bootstrap.ComboBox} combo This combo box
15119             */
15120         'tick' : true,
15121         /**
15122          * @event touchviewdisplay
15123          * Fires when touch view require special display (default is using displayField)
15124             * @param {Roo.bootstrap.ComboBox} combo This combo box
15125             * @param {Object} cfg set html .
15126             */
15127         'touchviewdisplay' : true
15128         
15129     });
15130     
15131     this.item = [];
15132     this.tickItems = [];
15133     
15134     this.selectedIndex = -1;
15135     if(this.mode == 'local'){
15136         if(config.queryDelay === undefined){
15137             this.queryDelay = 10;
15138         }
15139         if(config.minChars === undefined){
15140             this.minChars = 0;
15141         }
15142     }
15143 };
15144
15145 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15146      
15147     /**
15148      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15149      * rendering into an Roo.Editor, defaults to false)
15150      */
15151     /**
15152      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15153      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15154      */
15155     /**
15156      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15157      */
15158     /**
15159      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15160      * the dropdown list (defaults to undefined, with no header element)
15161      */
15162
15163      /**
15164      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15165      */
15166      
15167      /**
15168      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15169      */
15170     listWidth: undefined,
15171     /**
15172      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15173      * mode = 'remote' or 'text' if mode = 'local')
15174      */
15175     displayField: undefined,
15176     
15177     /**
15178      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15179      * mode = 'remote' or 'value' if mode = 'local'). 
15180      * Note: use of a valueField requires the user make a selection
15181      * in order for a value to be mapped.
15182      */
15183     valueField: undefined,
15184     /**
15185      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15186      */
15187     modalTitle : '',
15188     
15189     /**
15190      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15191      * field's data value (defaults to the underlying DOM element's name)
15192      */
15193     hiddenName: undefined,
15194     /**
15195      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15196      */
15197     listClass: '',
15198     /**
15199      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15200      */
15201     selectedClass: 'active',
15202     
15203     /**
15204      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15205      */
15206     shadow:'sides',
15207     /**
15208      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15209      * anchor positions (defaults to 'tl-bl')
15210      */
15211     listAlign: 'tl-bl?',
15212     /**
15213      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15214      */
15215     maxHeight: 300,
15216     /**
15217      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15218      * query specified by the allQuery config option (defaults to 'query')
15219      */
15220     triggerAction: 'query',
15221     /**
15222      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15223      * (defaults to 4, does not apply if editable = false)
15224      */
15225     minChars : 4,
15226     /**
15227      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15228      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15229      */
15230     typeAhead: false,
15231     /**
15232      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15233      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15234      */
15235     queryDelay: 500,
15236     /**
15237      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15238      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15239      */
15240     pageSize: 0,
15241     /**
15242      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15243      * when editable = true (defaults to false)
15244      */
15245     selectOnFocus:false,
15246     /**
15247      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15248      */
15249     queryParam: 'query',
15250     /**
15251      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15252      * when mode = 'remote' (defaults to 'Loading...')
15253      */
15254     loadingText: 'Loading...',
15255     /**
15256      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15257      */
15258     resizable: false,
15259     /**
15260      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15261      */
15262     handleHeight : 8,
15263     /**
15264      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15265      * traditional select (defaults to true)
15266      */
15267     editable: true,
15268     /**
15269      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15270      */
15271     allQuery: '',
15272     /**
15273      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15274      */
15275     mode: 'remote',
15276     /**
15277      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15278      * listWidth has a higher value)
15279      */
15280     minListWidth : 70,
15281     /**
15282      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15283      * allow the user to set arbitrary text into the field (defaults to false)
15284      */
15285     forceSelection:false,
15286     /**
15287      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15288      * if typeAhead = true (defaults to 250)
15289      */
15290     typeAheadDelay : 250,
15291     /**
15292      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15293      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15294      */
15295     valueNotFoundText : undefined,
15296     /**
15297      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15298      */
15299     blockFocus : false,
15300     
15301     /**
15302      * @cfg {Boolean} disableClear Disable showing of clear button.
15303      */
15304     disableClear : false,
15305     /**
15306      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15307      */
15308     alwaysQuery : false,
15309     
15310     /**
15311      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15312      */
15313     multiple : false,
15314     
15315     /**
15316      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15317      */
15318     invalidClass : "has-warning",
15319     
15320     /**
15321      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15322      */
15323     validClass : "has-success",
15324     
15325     /**
15326      * @cfg {Boolean} specialFilter (true|false) special filter default false
15327      */
15328     specialFilter : false,
15329     
15330     /**
15331      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15332      */
15333     mobileTouchView : true,
15334     
15335     /**
15336      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15337      */
15338     useNativeIOS : false,
15339     
15340     /**
15341      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15342      */
15343     mobile_restrict_height : false,
15344     
15345     ios_options : false,
15346     
15347     //private
15348     addicon : false,
15349     editicon: false,
15350     
15351     page: 0,
15352     hasQuery: false,
15353     append: false,
15354     loadNext: false,
15355     autoFocus : true,
15356     tickable : false,
15357     btnPosition : 'right',
15358     triggerList : true,
15359     showToggleBtn : true,
15360     animate : true,
15361     emptyResultText: 'Empty',
15362     triggerText : 'Select',
15363     emptyTitle : '',
15364     width : false,
15365     
15366     // element that contains real text value.. (when hidden is used..)
15367     
15368     getAutoCreate : function()
15369     {   
15370         var cfg = false;
15371         //render
15372         /*
15373          * Render classic select for iso
15374          */
15375         
15376         if(Roo.isIOS && this.useNativeIOS){
15377             cfg = this.getAutoCreateNativeIOS();
15378             return cfg;
15379         }
15380         
15381         /*
15382          * Touch Devices
15383          */
15384         
15385         if(Roo.isTouch && this.mobileTouchView){
15386             cfg = this.getAutoCreateTouchView();
15387             return cfg;;
15388         }
15389         
15390         /*
15391          *  Normal ComboBox
15392          */
15393         if(!this.tickable){
15394             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15395             return cfg;
15396         }
15397         
15398         /*
15399          *  ComboBox with tickable selections
15400          */
15401              
15402         var align = this.labelAlign || this.parentLabelAlign();
15403         
15404         cfg = {
15405             cls : 'form-group roo-combobox-tickable' //input-group
15406         };
15407         
15408         var btn_text_select = '';
15409         var btn_text_done = '';
15410         var btn_text_cancel = '';
15411         
15412         if (this.btn_text_show) {
15413             btn_text_select = 'Select';
15414             btn_text_done = 'Done';
15415             btn_text_cancel = 'Cancel'; 
15416         }
15417         
15418         var buttons = {
15419             tag : 'div',
15420             cls : 'tickable-buttons',
15421             cn : [
15422                 {
15423                     tag : 'button',
15424                     type : 'button',
15425                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15426                     //html : this.triggerText
15427                     html: btn_text_select
15428                 },
15429                 {
15430                     tag : 'button',
15431                     type : 'button',
15432                     name : 'ok',
15433                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15434                     //html : 'Done'
15435                     html: btn_text_done
15436                 },
15437                 {
15438                     tag : 'button',
15439                     type : 'button',
15440                     name : 'cancel',
15441                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15442                     //html : 'Cancel'
15443                     html: btn_text_cancel
15444                 }
15445             ]
15446         };
15447         
15448         if(this.editable){
15449             buttons.cn.unshift({
15450                 tag: 'input',
15451                 cls: 'roo-select2-search-field-input'
15452             });
15453         }
15454         
15455         var _this = this;
15456         
15457         Roo.each(buttons.cn, function(c){
15458             if (_this.size) {
15459                 c.cls += ' btn-' + _this.size;
15460             }
15461
15462             if (_this.disabled) {
15463                 c.disabled = true;
15464             }
15465         });
15466         
15467         var box = {
15468             tag: 'div',
15469             style : 'display: contents',
15470             cn: [
15471                 {
15472                     tag: 'input',
15473                     type : 'hidden',
15474                     cls: 'form-hidden-field'
15475                 },
15476                 {
15477                     tag: 'ul',
15478                     cls: 'roo-select2-choices',
15479                     cn:[
15480                         {
15481                             tag: 'li',
15482                             cls: 'roo-select2-search-field',
15483                             cn: [
15484                                 buttons
15485                             ]
15486                         }
15487                     ]
15488                 }
15489             ]
15490         };
15491         
15492         var combobox = {
15493             cls: 'roo-select2-container input-group roo-select2-container-multi',
15494             cn: [
15495                 
15496                 box
15497 //                {
15498 //                    tag: 'ul',
15499 //                    cls: 'typeahead typeahead-long dropdown-menu',
15500 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15501 //                }
15502             ]
15503         };
15504         
15505         if(this.hasFeedback && !this.allowBlank){
15506             
15507             var feedback = {
15508                 tag: 'span',
15509                 cls: 'glyphicon form-control-feedback'
15510             };
15511
15512             combobox.cn.push(feedback);
15513         }
15514         
15515         
15516         
15517         var indicator = {
15518             tag : 'i',
15519             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15520             tooltip : 'This field is required'
15521         };
15522         if (Roo.bootstrap.version == 4) {
15523             indicator = {
15524                 tag : 'i',
15525                 style : 'display:none'
15526             };
15527         }
15528         if (align ==='left' && this.fieldLabel.length) {
15529             
15530             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15531             
15532             cfg.cn = [
15533                 indicator,
15534                 {
15535                     tag: 'label',
15536                     'for' :  id,
15537                     cls : 'control-label col-form-label',
15538                     html : this.fieldLabel
15539
15540                 },
15541                 {
15542                     cls : "", 
15543                     cn: [
15544                         combobox
15545                     ]
15546                 }
15547
15548             ];
15549             
15550             var labelCfg = cfg.cn[1];
15551             var contentCfg = cfg.cn[2];
15552             
15553
15554             if(this.indicatorpos == 'right'){
15555                 
15556                 cfg.cn = [
15557                     {
15558                         tag: 'label',
15559                         'for' :  id,
15560                         cls : 'control-label col-form-label',
15561                         cn : [
15562                             {
15563                                 tag : 'span',
15564                                 html : this.fieldLabel
15565                             },
15566                             indicator
15567                         ]
15568                     },
15569                     {
15570                         cls : "",
15571                         cn: [
15572                             combobox
15573                         ]
15574                     }
15575
15576                 ];
15577                 
15578                 
15579                 
15580                 labelCfg = cfg.cn[0];
15581                 contentCfg = cfg.cn[1];
15582             
15583             }
15584             
15585             if(this.labelWidth > 12){
15586                 labelCfg.style = "width: " + this.labelWidth + 'px';
15587             }
15588             if(this.width * 1 > 0){
15589                 contentCfg.style = "width: " + this.width + 'px';
15590             }
15591             if(this.labelWidth < 13 && this.labelmd == 0){
15592                 this.labelmd = this.labelWidth;
15593             }
15594             
15595             if(this.labellg > 0){
15596                 labelCfg.cls += ' col-lg-' + this.labellg;
15597                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15598             }
15599             
15600             if(this.labelmd > 0){
15601                 labelCfg.cls += ' col-md-' + this.labelmd;
15602                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15603             }
15604             
15605             if(this.labelsm > 0){
15606                 labelCfg.cls += ' col-sm-' + this.labelsm;
15607                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15608             }
15609             
15610             if(this.labelxs > 0){
15611                 labelCfg.cls += ' col-xs-' + this.labelxs;
15612                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15613             }
15614                 
15615                 
15616         } else if ( this.fieldLabel.length) {
15617 //                Roo.log(" label");
15618                  cfg.cn = [
15619                    indicator,
15620                     {
15621                         tag: 'label',
15622                         //cls : 'input-group-addon',
15623                         html : this.fieldLabel
15624                     },
15625                     combobox
15626                 ];
15627                 
15628                 if(this.indicatorpos == 'right'){
15629                     cfg.cn = [
15630                         {
15631                             tag: 'label',
15632                             //cls : 'input-group-addon',
15633                             html : this.fieldLabel
15634                         },
15635                         indicator,
15636                         combobox
15637                     ];
15638                     
15639                 }
15640
15641         } else {
15642             
15643 //                Roo.log(" no label && no align");
15644                 cfg = combobox
15645                      
15646                 
15647         }
15648          
15649         var settings=this;
15650         ['xs','sm','md','lg'].map(function(size){
15651             if (settings[size]) {
15652                 cfg.cls += ' col-' + size + '-' + settings[size];
15653             }
15654         });
15655         
15656         return cfg;
15657         
15658     },
15659     
15660     _initEventsCalled : false,
15661     
15662     // private
15663     initEvents: function()
15664     {   
15665         if (this._initEventsCalled) { // as we call render... prevent looping...
15666             return;
15667         }
15668         this._initEventsCalled = true;
15669         
15670         if (!this.store) {
15671             throw "can not find store for combo";
15672         }
15673         
15674         this.indicator = this.indicatorEl();
15675         
15676         this.store = Roo.factory(this.store, Roo.data);
15677         this.store.parent = this;
15678         
15679         // if we are building from html. then this element is so complex, that we can not really
15680         // use the rendered HTML.
15681         // so we have to trash and replace the previous code.
15682         if (Roo.XComponent.build_from_html) {
15683             // remove this element....
15684             var e = this.el.dom, k=0;
15685             while (e ) { e = e.previousSibling;  ++k;}
15686
15687             this.el.remove();
15688             
15689             this.el=false;
15690             this.rendered = false;
15691             
15692             this.render(this.parent().getChildContainer(true), k);
15693         }
15694         
15695         if(Roo.isIOS && this.useNativeIOS){
15696             this.initIOSView();
15697             return;
15698         }
15699         
15700         /*
15701          * Touch Devices
15702          */
15703         
15704         if(Roo.isTouch && this.mobileTouchView){
15705             this.initTouchView();
15706             return;
15707         }
15708         
15709         if(this.tickable){
15710             this.initTickableEvents();
15711             return;
15712         }
15713         
15714         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15715         
15716         if(this.hiddenName){
15717             
15718             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15719             
15720             this.hiddenField.dom.value =
15721                 this.hiddenValue !== undefined ? this.hiddenValue :
15722                 this.value !== undefined ? this.value : '';
15723
15724             // prevent input submission
15725             this.el.dom.removeAttribute('name');
15726             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15727              
15728              
15729         }
15730         //if(Roo.isGecko){
15731         //    this.el.dom.setAttribute('autocomplete', 'off');
15732         //}
15733         
15734         var cls = 'x-combo-list';
15735         
15736         //this.list = new Roo.Layer({
15737         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15738         //});
15739         
15740         var _this = this;
15741         
15742         (function(){
15743             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15744             _this.list.setWidth(lw);
15745         }).defer(100);
15746         
15747         this.list.on('mouseover', this.onViewOver, this);
15748         this.list.on('mousemove', this.onViewMove, this);
15749         this.list.on('scroll', this.onViewScroll, this);
15750         
15751         /*
15752         this.list.swallowEvent('mousewheel');
15753         this.assetHeight = 0;
15754
15755         if(this.title){
15756             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15757             this.assetHeight += this.header.getHeight();
15758         }
15759
15760         this.innerList = this.list.createChild({cls:cls+'-inner'});
15761         this.innerList.on('mouseover', this.onViewOver, this);
15762         this.innerList.on('mousemove', this.onViewMove, this);
15763         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15764         
15765         if(this.allowBlank && !this.pageSize && !this.disableClear){
15766             this.footer = this.list.createChild({cls:cls+'-ft'});
15767             this.pageTb = new Roo.Toolbar(this.footer);
15768            
15769         }
15770         if(this.pageSize){
15771             this.footer = this.list.createChild({cls:cls+'-ft'});
15772             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15773                     {pageSize: this.pageSize});
15774             
15775         }
15776         
15777         if (this.pageTb && this.allowBlank && !this.disableClear) {
15778             var _this = this;
15779             this.pageTb.add(new Roo.Toolbar.Fill(), {
15780                 cls: 'x-btn-icon x-btn-clear',
15781                 text: '&#160;',
15782                 handler: function()
15783                 {
15784                     _this.collapse();
15785                     _this.clearValue();
15786                     _this.onSelect(false, -1);
15787                 }
15788             });
15789         }
15790         if (this.footer) {
15791             this.assetHeight += this.footer.getHeight();
15792         }
15793         */
15794             
15795         if(!this.tpl){
15796             this.tpl = Roo.bootstrap.version == 4 ?
15797                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15798                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15799         }
15800
15801         this.view = new Roo.View(this.list, this.tpl, {
15802             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15803         });
15804         //this.view.wrapEl.setDisplayed(false);
15805         this.view.on('click', this.onViewClick, this);
15806         
15807         
15808         this.store.on('beforeload', this.onBeforeLoad, this);
15809         this.store.on('load', this.onLoad, this);
15810         this.store.on('loadexception', this.onLoadException, this);
15811         /*
15812         if(this.resizable){
15813             this.resizer = new Roo.Resizable(this.list,  {
15814                pinned:true, handles:'se'
15815             });
15816             this.resizer.on('resize', function(r, w, h){
15817                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15818                 this.listWidth = w;
15819                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15820                 this.restrictHeight();
15821             }, this);
15822             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15823         }
15824         */
15825         if(!this.editable){
15826             this.editable = true;
15827             this.setEditable(false);
15828         }
15829         
15830         /*
15831         
15832         if (typeof(this.events.add.listeners) != 'undefined') {
15833             
15834             this.addicon = this.wrap.createChild(
15835                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15836        
15837             this.addicon.on('click', function(e) {
15838                 this.fireEvent('add', this);
15839             }, this);
15840         }
15841         if (typeof(this.events.edit.listeners) != 'undefined') {
15842             
15843             this.editicon = this.wrap.createChild(
15844                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15845             if (this.addicon) {
15846                 this.editicon.setStyle('margin-left', '40px');
15847             }
15848             this.editicon.on('click', function(e) {
15849                 
15850                 // we fire even  if inothing is selected..
15851                 this.fireEvent('edit', this, this.lastData );
15852                 
15853             }, this);
15854         }
15855         */
15856         
15857         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15858             "up" : function(e){
15859                 this.inKeyMode = true;
15860                 this.selectPrev();
15861             },
15862
15863             "down" : function(e){
15864                 if(!this.isExpanded()){
15865                     this.onTriggerClick();
15866                 }else{
15867                     this.inKeyMode = true;
15868                     this.selectNext();
15869                 }
15870             },
15871
15872             "enter" : function(e){
15873 //                this.onViewClick();
15874                 //return true;
15875                 this.collapse();
15876                 
15877                 if(this.fireEvent("specialkey", this, e)){
15878                     this.onViewClick(false);
15879                 }
15880                 
15881                 return true;
15882             },
15883
15884             "esc" : function(e){
15885                 this.collapse();
15886             },
15887
15888             "tab" : function(e){
15889                 this.collapse();
15890                 
15891                 if(this.fireEvent("specialkey", this, e)){
15892                     this.onViewClick(false);
15893                 }
15894                 
15895                 return true;
15896             },
15897
15898             scope : this,
15899
15900             doRelay : function(foo, bar, hname){
15901                 if(hname == 'down' || this.scope.isExpanded()){
15902                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15903                 }
15904                 return true;
15905             },
15906
15907             forceKeyDown: true
15908         });
15909         
15910         
15911         this.queryDelay = Math.max(this.queryDelay || 10,
15912                 this.mode == 'local' ? 10 : 250);
15913         
15914         
15915         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15916         
15917         if(this.typeAhead){
15918             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15919         }
15920         if(this.editable !== false){
15921             this.inputEl().on("keyup", this.onKeyUp, this);
15922         }
15923         if(this.forceSelection){
15924             this.inputEl().on('blur', this.doForce, this);
15925         }
15926         
15927         if(this.multiple){
15928             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15929             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15930         }
15931     },
15932     
15933     initTickableEvents: function()
15934     {   
15935         this.createList();
15936         
15937         if(this.hiddenName){
15938             
15939             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15940             
15941             this.hiddenField.dom.value =
15942                 this.hiddenValue !== undefined ? this.hiddenValue :
15943                 this.value !== undefined ? this.value : '';
15944
15945             // prevent input submission
15946             this.el.dom.removeAttribute('name');
15947             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15948              
15949              
15950         }
15951         
15952 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15953         
15954         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15955         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15956         if(this.triggerList){
15957             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15958         }
15959          
15960         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15961         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15962         
15963         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15964         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15965         
15966         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15967         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15968         
15969         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15970         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15971         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15972         
15973         this.okBtn.hide();
15974         this.cancelBtn.hide();
15975         
15976         var _this = this;
15977         
15978         (function(){
15979             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15980             _this.list.setWidth(lw);
15981         }).defer(100);
15982         
15983         this.list.on('mouseover', this.onViewOver, this);
15984         this.list.on('mousemove', this.onViewMove, this);
15985         
15986         this.list.on('scroll', this.onViewScroll, this);
15987         
15988         if(!this.tpl){
15989             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15990                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15991         }
15992
15993         this.view = new Roo.View(this.list, this.tpl, {
15994             singleSelect:true,
15995             tickable:true,
15996             parent:this,
15997             store: this.store,
15998             selectedClass: this.selectedClass
15999         });
16000         
16001         //this.view.wrapEl.setDisplayed(false);
16002         this.view.on('click', this.onViewClick, this);
16003         
16004         
16005         
16006         this.store.on('beforeload', this.onBeforeLoad, this);
16007         this.store.on('load', this.onLoad, this);
16008         this.store.on('loadexception', this.onLoadException, this);
16009         
16010         if(this.editable){
16011             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16012                 "up" : function(e){
16013                     this.inKeyMode = true;
16014                     this.selectPrev();
16015                 },
16016
16017                 "down" : function(e){
16018                     this.inKeyMode = true;
16019                     this.selectNext();
16020                 },
16021
16022                 "enter" : function(e){
16023                     if(this.fireEvent("specialkey", this, e)){
16024                         this.onViewClick(false);
16025                     }
16026                     
16027                     return true;
16028                 },
16029
16030                 "esc" : function(e){
16031                     this.onTickableFooterButtonClick(e, false, false);
16032                 },
16033
16034                 "tab" : function(e){
16035                     this.fireEvent("specialkey", this, e);
16036                     
16037                     this.onTickableFooterButtonClick(e, false, false);
16038                     
16039                     return true;
16040                 },
16041
16042                 scope : this,
16043
16044                 doRelay : function(e, fn, key){
16045                     if(this.scope.isExpanded()){
16046                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16047                     }
16048                     return true;
16049                 },
16050
16051                 forceKeyDown: true
16052             });
16053         }
16054         
16055         this.queryDelay = Math.max(this.queryDelay || 10,
16056                 this.mode == 'local' ? 10 : 250);
16057         
16058         
16059         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16060         
16061         if(this.typeAhead){
16062             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16063         }
16064         
16065         if(this.editable !== false){
16066             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16067         }
16068         
16069         this.indicator = this.indicatorEl();
16070         
16071         if(this.indicator){
16072             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16073             this.indicator.hide();
16074         }
16075         
16076     },
16077
16078     onDestroy : function(){
16079         if(this.view){
16080             this.view.setStore(null);
16081             this.view.el.removeAllListeners();
16082             this.view.el.remove();
16083             this.view.purgeListeners();
16084         }
16085         if(this.list){
16086             this.list.dom.innerHTML  = '';
16087         }
16088         
16089         if(this.store){
16090             this.store.un('beforeload', this.onBeforeLoad, this);
16091             this.store.un('load', this.onLoad, this);
16092             this.store.un('loadexception', this.onLoadException, this);
16093         }
16094         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16095     },
16096
16097     // private
16098     fireKey : function(e){
16099         if(e.isNavKeyPress() && !this.list.isVisible()){
16100             this.fireEvent("specialkey", this, e);
16101         }
16102     },
16103
16104     // private
16105     onResize: function(w, h)
16106     {
16107         
16108         
16109 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16110 //        
16111 //        if(typeof w != 'number'){
16112 //            // we do not handle it!?!?
16113 //            return;
16114 //        }
16115 //        var tw = this.trigger.getWidth();
16116 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16117 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16118 //        var x = w - tw;
16119 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16120 //            
16121 //        //this.trigger.setStyle('left', x+'px');
16122 //        
16123 //        if(this.list && this.listWidth === undefined){
16124 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16125 //            this.list.setWidth(lw);
16126 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16127 //        }
16128         
16129     
16130         
16131     },
16132
16133     /**
16134      * Allow or prevent the user from directly editing the field text.  If false is passed,
16135      * the user will only be able to select from the items defined in the dropdown list.  This method
16136      * is the runtime equivalent of setting the 'editable' config option at config time.
16137      * @param {Boolean} value True to allow the user to directly edit the field text
16138      */
16139     setEditable : function(value){
16140         if(value == this.editable){
16141             return;
16142         }
16143         this.editable = value;
16144         if(!value){
16145             this.inputEl().dom.setAttribute('readOnly', true);
16146             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16147             this.inputEl().addClass('x-combo-noedit');
16148         }else{
16149             this.inputEl().dom.setAttribute('readOnly', false);
16150             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16151             this.inputEl().removeClass('x-combo-noedit');
16152         }
16153     },
16154
16155     // private
16156     
16157     onBeforeLoad : function(combo,opts){
16158         if(!this.hasFocus){
16159             return;
16160         }
16161          if (!opts.add) {
16162             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16163          }
16164         this.restrictHeight();
16165         this.selectedIndex = -1;
16166     },
16167
16168     // private
16169     onLoad : function(){
16170         
16171         this.hasQuery = false;
16172         
16173         if(!this.hasFocus){
16174             return;
16175         }
16176         
16177         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16178             this.loading.hide();
16179         }
16180         
16181         if(this.store.getCount() > 0){
16182             
16183             this.expand();
16184             this.restrictHeight();
16185             if(this.lastQuery == this.allQuery){
16186                 if(this.editable && !this.tickable){
16187                     this.inputEl().dom.select();
16188                 }
16189                 
16190                 if(
16191                     !this.selectByValue(this.value, true) &&
16192                     this.autoFocus && 
16193                     (
16194                         !this.store.lastOptions ||
16195                         typeof(this.store.lastOptions.add) == 'undefined' || 
16196                         this.store.lastOptions.add != true
16197                     )
16198                 ){
16199                     this.select(0, true);
16200                 }
16201             }else{
16202                 if(this.autoFocus){
16203                     this.selectNext();
16204                 }
16205                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16206                     this.taTask.delay(this.typeAheadDelay);
16207                 }
16208             }
16209         }else{
16210             this.onEmptyResults();
16211         }
16212         
16213         //this.el.focus();
16214     },
16215     // private
16216     onLoadException : function()
16217     {
16218         this.hasQuery = false;
16219         
16220         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16221             this.loading.hide();
16222         }
16223         
16224         if(this.tickable && this.editable){
16225             return;
16226         }
16227         
16228         this.collapse();
16229         // only causes errors at present
16230         //Roo.log(this.store.reader.jsonData);
16231         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16232             // fixme
16233             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16234         //}
16235         
16236         
16237     },
16238     // private
16239     onTypeAhead : function(){
16240         if(this.store.getCount() > 0){
16241             var r = this.store.getAt(0);
16242             var newValue = r.data[this.displayField];
16243             var len = newValue.length;
16244             var selStart = this.getRawValue().length;
16245             
16246             if(selStart != len){
16247                 this.setRawValue(newValue);
16248                 this.selectText(selStart, newValue.length);
16249             }
16250         }
16251     },
16252
16253     // private
16254     onSelect : function(record, index){
16255         
16256         if(this.fireEvent('beforeselect', this, record, index) !== false){
16257         
16258             this.setFromData(index > -1 ? record.data : false);
16259             
16260             this.collapse();
16261             this.fireEvent('select', this, record, index);
16262         }
16263     },
16264
16265     /**
16266      * Returns the currently selected field value or empty string if no value is set.
16267      * @return {String} value The selected value
16268      */
16269     getValue : function()
16270     {
16271         if(Roo.isIOS && this.useNativeIOS){
16272             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16273         }
16274         
16275         if(this.multiple){
16276             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16277         }
16278         
16279         if(this.valueField){
16280             return typeof this.value != 'undefined' ? this.value : '';
16281         }else{
16282             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16283         }
16284     },
16285     
16286     getRawValue : function()
16287     {
16288         if(Roo.isIOS && this.useNativeIOS){
16289             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16290         }
16291         
16292         var v = this.inputEl().getValue();
16293         
16294         return v;
16295     },
16296
16297     /**
16298      * Clears any text/value currently set in the field
16299      */
16300     clearValue : function(){
16301         
16302         if(this.hiddenField){
16303             this.hiddenField.dom.value = '';
16304         }
16305         this.value = '';
16306         this.setRawValue('');
16307         this.lastSelectionText = '';
16308         this.lastData = false;
16309         
16310         var close = this.closeTriggerEl();
16311         
16312         if(close){
16313             close.hide();
16314         }
16315         
16316         this.validate();
16317         
16318     },
16319
16320     /**
16321      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16322      * will be displayed in the field.  If the value does not match the data value of an existing item,
16323      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16324      * Otherwise the field will be blank (although the value will still be set).
16325      * @param {String} value The value to match
16326      */
16327     setValue : function(v)
16328     {
16329         if(Roo.isIOS && this.useNativeIOS){
16330             this.setIOSValue(v);
16331             return;
16332         }
16333         
16334         if(this.multiple){
16335             this.syncValue();
16336             return;
16337         }
16338         
16339         var text = v;
16340         if(this.valueField){
16341             var r = this.findRecord(this.valueField, v);
16342             if(r){
16343                 text = r.data[this.displayField];
16344             }else if(this.valueNotFoundText !== undefined){
16345                 text = this.valueNotFoundText;
16346             }
16347         }
16348         this.lastSelectionText = text;
16349         if(this.hiddenField){
16350             this.hiddenField.dom.value = v;
16351         }
16352         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16353         this.value = v;
16354         
16355         var close = this.closeTriggerEl();
16356         
16357         if(close){
16358             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16359         }
16360         
16361         this.validate();
16362     },
16363     /**
16364      * @property {Object} the last set data for the element
16365      */
16366     
16367     lastData : false,
16368     /**
16369      * Sets the value of the field based on a object which is related to the record format for the store.
16370      * @param {Object} value the value to set as. or false on reset?
16371      */
16372     setFromData : function(o){
16373         
16374         if(this.multiple){
16375             this.addItem(o);
16376             return;
16377         }
16378             
16379         var dv = ''; // display value
16380         var vv = ''; // value value..
16381         this.lastData = o;
16382         if (this.displayField) {
16383             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16384         } else {
16385             // this is an error condition!!!
16386             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16387         }
16388         
16389         if(this.valueField){
16390             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16391         }
16392         
16393         var close = this.closeTriggerEl();
16394         
16395         if(close){
16396             if(dv.length || vv * 1 > 0){
16397                 close.show() ;
16398                 this.blockFocus=true;
16399             } else {
16400                 close.hide();
16401             }             
16402         }
16403         
16404         if(this.hiddenField){
16405             this.hiddenField.dom.value = vv;
16406             
16407             this.lastSelectionText = dv;
16408             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16409             this.value = vv;
16410             return;
16411         }
16412         // no hidden field.. - we store the value in 'value', but still display
16413         // display field!!!!
16414         this.lastSelectionText = dv;
16415         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16416         this.value = vv;
16417         
16418         
16419         
16420     },
16421     // private
16422     reset : function(){
16423         // overridden so that last data is reset..
16424         
16425         if(this.multiple){
16426             this.clearItem();
16427             return;
16428         }
16429         
16430         this.setValue(this.originalValue);
16431         //this.clearInvalid();
16432         this.lastData = false;
16433         if (this.view) {
16434             this.view.clearSelections();
16435         }
16436         
16437         this.validate();
16438     },
16439     // private
16440     findRecord : function(prop, value){
16441         var record;
16442         if(this.store.getCount() > 0){
16443             this.store.each(function(r){
16444                 if(r.data[prop] == value){
16445                     record = r;
16446                     return false;
16447                 }
16448                 return true;
16449             });
16450         }
16451         return record;
16452     },
16453     
16454     getName: function()
16455     {
16456         // returns hidden if it's set..
16457         if (!this.rendered) {return ''};
16458         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16459         
16460     },
16461     // private
16462     onViewMove : function(e, t){
16463         this.inKeyMode = false;
16464     },
16465
16466     // private
16467     onViewOver : function(e, t){
16468         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16469             return;
16470         }
16471         var item = this.view.findItemFromChild(t);
16472         
16473         if(item){
16474             var index = this.view.indexOf(item);
16475             this.select(index, false);
16476         }
16477     },
16478
16479     // private
16480     onViewClick : function(view, doFocus, el, e)
16481     {
16482         var index = this.view.getSelectedIndexes()[0];
16483         
16484         var r = this.store.getAt(index);
16485         
16486         if(this.tickable){
16487             
16488             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16489                 return;
16490             }
16491             
16492             var rm = false;
16493             var _this = this;
16494             
16495             Roo.each(this.tickItems, function(v,k){
16496                 
16497                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16498                     Roo.log(v);
16499                     _this.tickItems.splice(k, 1);
16500                     
16501                     if(typeof(e) == 'undefined' && view == false){
16502                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16503                     }
16504                     
16505                     rm = true;
16506                     return;
16507                 }
16508             });
16509             
16510             if(rm){
16511                 return;
16512             }
16513             
16514             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16515                 this.tickItems.push(r.data);
16516             }
16517             
16518             if(typeof(e) == 'undefined' && view == false){
16519                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16520             }
16521                     
16522             return;
16523         }
16524         
16525         if(r){
16526             this.onSelect(r, index);
16527         }
16528         if(doFocus !== false && !this.blockFocus){
16529             this.inputEl().focus();
16530         }
16531     },
16532
16533     // private
16534     restrictHeight : function(){
16535         //this.innerList.dom.style.height = '';
16536         //var inner = this.innerList.dom;
16537         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16538         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16539         //this.list.beginUpdate();
16540         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16541         this.list.alignTo(this.inputEl(), this.listAlign);
16542         this.list.alignTo(this.inputEl(), this.listAlign);
16543         //this.list.endUpdate();
16544     },
16545
16546     // private
16547     onEmptyResults : function(){
16548         
16549         if(this.tickable && this.editable){
16550             this.hasFocus = false;
16551             this.restrictHeight();
16552             return;
16553         }
16554         
16555         this.collapse();
16556     },
16557
16558     /**
16559      * Returns true if the dropdown list is expanded, else false.
16560      */
16561     isExpanded : function(){
16562         return this.list.isVisible();
16563     },
16564
16565     /**
16566      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16567      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16568      * @param {String} value The data value of the item to select
16569      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16570      * selected item if it is not currently in view (defaults to true)
16571      * @return {Boolean} True if the value matched an item in the list, else false
16572      */
16573     selectByValue : function(v, scrollIntoView){
16574         if(v !== undefined && v !== null){
16575             var r = this.findRecord(this.valueField || this.displayField, v);
16576             if(r){
16577                 this.select(this.store.indexOf(r), scrollIntoView);
16578                 return true;
16579             }
16580         }
16581         return false;
16582     },
16583
16584     /**
16585      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16586      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16587      * @param {Number} index The zero-based index of the list item to select
16588      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16589      * selected item if it is not currently in view (defaults to true)
16590      */
16591     select : function(index, scrollIntoView){
16592         this.selectedIndex = index;
16593         this.view.select(index);
16594         if(scrollIntoView !== false){
16595             var el = this.view.getNode(index);
16596             /*
16597              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16598              */
16599             if(el){
16600                 this.list.scrollChildIntoView(el, false);
16601             }
16602         }
16603     },
16604
16605     // private
16606     selectNext : function(){
16607         var ct = this.store.getCount();
16608         if(ct > 0){
16609             if(this.selectedIndex == -1){
16610                 this.select(0);
16611             }else if(this.selectedIndex < ct-1){
16612                 this.select(this.selectedIndex+1);
16613             }
16614         }
16615     },
16616
16617     // private
16618     selectPrev : function(){
16619         var ct = this.store.getCount();
16620         if(ct > 0){
16621             if(this.selectedIndex == -1){
16622                 this.select(0);
16623             }else if(this.selectedIndex != 0){
16624                 this.select(this.selectedIndex-1);
16625             }
16626         }
16627     },
16628
16629     // private
16630     onKeyUp : function(e){
16631         if(this.editable !== false && !e.isSpecialKey()){
16632             this.lastKey = e.getKey();
16633             this.dqTask.delay(this.queryDelay);
16634         }
16635     },
16636
16637     // private
16638     validateBlur : function(){
16639         return !this.list || !this.list.isVisible();   
16640     },
16641
16642     // private
16643     initQuery : function(){
16644         
16645         var v = this.getRawValue();
16646         
16647         if(this.tickable && this.editable){
16648             v = this.tickableInputEl().getValue();
16649         }
16650         
16651         this.doQuery(v);
16652     },
16653
16654     // private
16655     doForce : function(){
16656         if(this.inputEl().dom.value.length > 0){
16657             this.inputEl().dom.value =
16658                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16659              
16660         }
16661     },
16662
16663     /**
16664      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16665      * query allowing the query action to be canceled if needed.
16666      * @param {String} query The SQL query to execute
16667      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16668      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16669      * saved in the current store (defaults to false)
16670      */
16671     doQuery : function(q, forceAll){
16672         
16673         if(q === undefined || q === null){
16674             q = '';
16675         }
16676         var qe = {
16677             query: q,
16678             forceAll: forceAll,
16679             combo: this,
16680             cancel:false
16681         };
16682         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16683             return false;
16684         }
16685         q = qe.query;
16686         
16687         forceAll = qe.forceAll;
16688         if(forceAll === true || (q.length >= this.minChars)){
16689             
16690             this.hasQuery = true;
16691             
16692             if(this.lastQuery != q || this.alwaysQuery){
16693                 this.lastQuery = q;
16694                 if(this.mode == 'local'){
16695                     this.selectedIndex = -1;
16696                     if(forceAll){
16697                         this.store.clearFilter();
16698                     }else{
16699                         
16700                         if(this.specialFilter){
16701                             this.fireEvent('specialfilter', this);
16702                             this.onLoad();
16703                             return;
16704                         }
16705                         
16706                         this.store.filter(this.displayField, q);
16707                     }
16708                     
16709                     this.store.fireEvent("datachanged", this.store);
16710                     
16711                     this.onLoad();
16712                     
16713                     
16714                 }else{
16715                     
16716                     this.store.baseParams[this.queryParam] = q;
16717                     
16718                     var options = {params : this.getParams(q)};
16719                     
16720                     if(this.loadNext){
16721                         options.add = true;
16722                         options.params.start = this.page * this.pageSize;
16723                     }
16724                     
16725                     this.store.load(options);
16726                     
16727                     /*
16728                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16729                      *  we should expand the list on onLoad
16730                      *  so command out it
16731                      */
16732 //                    this.expand();
16733                 }
16734             }else{
16735                 this.selectedIndex = -1;
16736                 this.onLoad();   
16737             }
16738         }
16739         
16740         this.loadNext = false;
16741     },
16742     
16743     // private
16744     getParams : function(q){
16745         var p = {};
16746         //p[this.queryParam] = q;
16747         
16748         if(this.pageSize){
16749             p.start = 0;
16750             p.limit = this.pageSize;
16751         }
16752         return p;
16753     },
16754
16755     /**
16756      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16757      */
16758     collapse : function(){
16759         if(!this.isExpanded()){
16760             return;
16761         }
16762         
16763         this.list.hide();
16764         
16765         this.hasFocus = false;
16766         
16767         if(this.tickable){
16768             this.okBtn.hide();
16769             this.cancelBtn.hide();
16770             this.trigger.show();
16771             
16772             if(this.editable){
16773                 this.tickableInputEl().dom.value = '';
16774                 this.tickableInputEl().blur();
16775             }
16776             
16777         }
16778         
16779         Roo.get(document).un('mousedown', this.collapseIf, this);
16780         Roo.get(document).un('mousewheel', this.collapseIf, this);
16781         if (!this.editable) {
16782             Roo.get(document).un('keydown', this.listKeyPress, this);
16783         }
16784         this.fireEvent('collapse', this);
16785         
16786         this.validate();
16787     },
16788
16789     // private
16790     collapseIf : function(e){
16791         var in_combo  = e.within(this.el);
16792         var in_list =  e.within(this.list);
16793         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16794         
16795         if (in_combo || in_list || is_list) {
16796             //e.stopPropagation();
16797             return;
16798         }
16799         
16800         if(this.tickable){
16801             this.onTickableFooterButtonClick(e, false, false);
16802         }
16803
16804         this.collapse();
16805         
16806     },
16807
16808     /**
16809      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16810      */
16811     expand : function(){
16812        
16813         if(this.isExpanded() || !this.hasFocus){
16814             return;
16815         }
16816         
16817         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16818         this.list.setWidth(lw);
16819         
16820         Roo.log('expand');
16821         
16822         this.list.show();
16823         
16824         this.restrictHeight();
16825         
16826         if(this.tickable){
16827             
16828             this.tickItems = Roo.apply([], this.item);
16829             
16830             this.okBtn.show();
16831             this.cancelBtn.show();
16832             this.trigger.hide();
16833             
16834             if(this.editable){
16835                 this.tickableInputEl().focus();
16836             }
16837             
16838         }
16839         
16840         Roo.get(document).on('mousedown', this.collapseIf, this);
16841         Roo.get(document).on('mousewheel', this.collapseIf, this);
16842         if (!this.editable) {
16843             Roo.get(document).on('keydown', this.listKeyPress, this);
16844         }
16845         
16846         this.fireEvent('expand', this);
16847     },
16848
16849     // private
16850     // Implements the default empty TriggerField.onTriggerClick function
16851     onTriggerClick : function(e)
16852     {
16853         Roo.log('trigger click');
16854         
16855         if(this.disabled || !this.triggerList){
16856             return;
16857         }
16858         
16859         this.page = 0;
16860         this.loadNext = false;
16861         
16862         if(this.isExpanded()){
16863             this.collapse();
16864             if (!this.blockFocus) {
16865                 this.inputEl().focus();
16866             }
16867             
16868         }else {
16869             this.hasFocus = true;
16870             if(this.triggerAction == 'all') {
16871                 this.doQuery(this.allQuery, true);
16872             } else {
16873                 this.doQuery(this.getRawValue());
16874             }
16875             if (!this.blockFocus) {
16876                 this.inputEl().focus();
16877             }
16878         }
16879     },
16880     
16881     onTickableTriggerClick : function(e)
16882     {
16883         if(this.disabled){
16884             return;
16885         }
16886         
16887         this.page = 0;
16888         this.loadNext = false;
16889         this.hasFocus = true;
16890         
16891         if(this.triggerAction == 'all') {
16892             this.doQuery(this.allQuery, true);
16893         } else {
16894             this.doQuery(this.getRawValue());
16895         }
16896     },
16897     
16898     onSearchFieldClick : function(e)
16899     {
16900         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16901             this.onTickableFooterButtonClick(e, false, false);
16902             return;
16903         }
16904         
16905         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16906             return;
16907         }
16908         
16909         this.page = 0;
16910         this.loadNext = false;
16911         this.hasFocus = true;
16912         
16913         if(this.triggerAction == 'all') {
16914             this.doQuery(this.allQuery, true);
16915         } else {
16916             this.doQuery(this.getRawValue());
16917         }
16918     },
16919     
16920     listKeyPress : function(e)
16921     {
16922         //Roo.log('listkeypress');
16923         // scroll to first matching element based on key pres..
16924         if (e.isSpecialKey()) {
16925             return false;
16926         }
16927         var k = String.fromCharCode(e.getKey()).toUpperCase();
16928         //Roo.log(k);
16929         var match  = false;
16930         var csel = this.view.getSelectedNodes();
16931         var cselitem = false;
16932         if (csel.length) {
16933             var ix = this.view.indexOf(csel[0]);
16934             cselitem  = this.store.getAt(ix);
16935             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16936                 cselitem = false;
16937             }
16938             
16939         }
16940         
16941         this.store.each(function(v) { 
16942             if (cselitem) {
16943                 // start at existing selection.
16944                 if (cselitem.id == v.id) {
16945                     cselitem = false;
16946                 }
16947                 return true;
16948             }
16949                 
16950             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16951                 match = this.store.indexOf(v);
16952                 return false;
16953             }
16954             return true;
16955         }, this);
16956         
16957         if (match === false) {
16958             return true; // no more action?
16959         }
16960         // scroll to?
16961         this.view.select(match);
16962         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16963         sn.scrollIntoView(sn.dom.parentNode, false);
16964     },
16965     
16966     onViewScroll : function(e, t){
16967         
16968         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){
16969             return;
16970         }
16971         
16972         this.hasQuery = true;
16973         
16974         this.loading = this.list.select('.loading', true).first();
16975         
16976         if(this.loading === null){
16977             this.list.createChild({
16978                 tag: 'div',
16979                 cls: 'loading roo-select2-more-results roo-select2-active',
16980                 html: 'Loading more results...'
16981             });
16982             
16983             this.loading = this.list.select('.loading', true).first();
16984             
16985             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16986             
16987             this.loading.hide();
16988         }
16989         
16990         this.loading.show();
16991         
16992         var _combo = this;
16993         
16994         this.page++;
16995         this.loadNext = true;
16996         
16997         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16998         
16999         return;
17000     },
17001     
17002     addItem : function(o)
17003     {   
17004         var dv = ''; // display value
17005         
17006         if (this.displayField) {
17007             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17008         } else {
17009             // this is an error condition!!!
17010             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17011         }
17012         
17013         if(!dv.length){
17014             return;
17015         }
17016         
17017         var choice = this.choices.createChild({
17018             tag: 'li',
17019             cls: 'roo-select2-search-choice',
17020             cn: [
17021                 {
17022                     tag: 'div',
17023                     html: dv
17024                 },
17025                 {
17026                     tag: 'a',
17027                     href: '#',
17028                     cls: 'roo-select2-search-choice-close fa fa-times',
17029                     tabindex: '-1'
17030                 }
17031             ]
17032             
17033         }, this.searchField);
17034         
17035         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17036         
17037         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17038         
17039         this.item.push(o);
17040         
17041         this.lastData = o;
17042         
17043         this.syncValue();
17044         
17045         this.inputEl().dom.value = '';
17046         
17047         this.validate();
17048     },
17049     
17050     onRemoveItem : function(e, _self, o)
17051     {
17052         e.preventDefault();
17053         
17054         this.lastItem = Roo.apply([], this.item);
17055         
17056         var index = this.item.indexOf(o.data) * 1;
17057         
17058         if( index < 0){
17059             Roo.log('not this item?!');
17060             return;
17061         }
17062         
17063         this.item.splice(index, 1);
17064         o.item.remove();
17065         
17066         this.syncValue();
17067         
17068         this.fireEvent('remove', this, e);
17069         
17070         this.validate();
17071         
17072     },
17073     
17074     syncValue : function()
17075     {
17076         if(!this.item.length){
17077             this.clearValue();
17078             return;
17079         }
17080             
17081         var value = [];
17082         var _this = this;
17083         Roo.each(this.item, function(i){
17084             if(_this.valueField){
17085                 value.push(i[_this.valueField]);
17086                 return;
17087             }
17088
17089             value.push(i);
17090         });
17091
17092         this.value = value.join(',');
17093
17094         if(this.hiddenField){
17095             this.hiddenField.dom.value = this.value;
17096         }
17097         
17098         this.store.fireEvent("datachanged", this.store);
17099         
17100         this.validate();
17101     },
17102     
17103     clearItem : function()
17104     {
17105         if(!this.multiple){
17106             return;
17107         }
17108         
17109         this.item = [];
17110         
17111         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17112            c.remove();
17113         });
17114         
17115         this.syncValue();
17116         
17117         this.validate();
17118         
17119         if(this.tickable && !Roo.isTouch){
17120             this.view.refresh();
17121         }
17122     },
17123     
17124     inputEl: function ()
17125     {
17126         if(Roo.isIOS && this.useNativeIOS){
17127             return this.el.select('select.roo-ios-select', true).first();
17128         }
17129         
17130         if(Roo.isTouch && this.mobileTouchView){
17131             return this.el.select('input.form-control',true).first();
17132         }
17133         
17134         if(this.tickable){
17135             return this.searchField;
17136         }
17137         
17138         return this.el.select('input.form-control',true).first();
17139     },
17140     
17141     onTickableFooterButtonClick : function(e, btn, el)
17142     {
17143         e.preventDefault();
17144         
17145         this.lastItem = Roo.apply([], this.item);
17146         
17147         if(btn && btn.name == 'cancel'){
17148             this.tickItems = Roo.apply([], this.item);
17149             this.collapse();
17150             return;
17151         }
17152         
17153         this.clearItem();
17154         
17155         var _this = this;
17156         
17157         Roo.each(this.tickItems, function(o){
17158             _this.addItem(o);
17159         });
17160         
17161         this.collapse();
17162         
17163     },
17164     
17165     validate : function()
17166     {
17167         if(this.getVisibilityEl().hasClass('hidden')){
17168             return true;
17169         }
17170         
17171         var v = this.getRawValue();
17172         
17173         if(this.multiple){
17174             v = this.getValue();
17175         }
17176         
17177         if(this.disabled || this.allowBlank || v.length){
17178             this.markValid();
17179             return true;
17180         }
17181         
17182         this.markInvalid();
17183         return false;
17184     },
17185     
17186     tickableInputEl : function()
17187     {
17188         if(!this.tickable || !this.editable){
17189             return this.inputEl();
17190         }
17191         
17192         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17193     },
17194     
17195     
17196     getAutoCreateTouchView : function()
17197     {
17198         var id = Roo.id();
17199         
17200         var cfg = {
17201             cls: 'form-group' //input-group
17202         };
17203         
17204         var input =  {
17205             tag: 'input',
17206             id : id,
17207             type : this.inputType,
17208             cls : 'form-control x-combo-noedit',
17209             autocomplete: 'new-password',
17210             placeholder : this.placeholder || '',
17211             readonly : true
17212         };
17213         
17214         if (this.name) {
17215             input.name = this.name;
17216         }
17217         
17218         if (this.size) {
17219             input.cls += ' input-' + this.size;
17220         }
17221         
17222         if (this.disabled) {
17223             input.disabled = true;
17224         }
17225         
17226         var inputblock = {
17227             cls : 'roo-combobox-wrap',
17228             cn : [
17229                 input
17230             ]
17231         };
17232         
17233         if(this.before){
17234             inputblock.cls += ' input-group';
17235             
17236             inputblock.cn.unshift({
17237                 tag :'span',
17238                 cls : 'input-group-addon input-group-prepend input-group-text',
17239                 html : this.before
17240             });
17241         }
17242         
17243         if(this.removable && !this.multiple){
17244             inputblock.cls += ' roo-removable';
17245             
17246             inputblock.cn.push({
17247                 tag: 'button',
17248                 html : 'x',
17249                 cls : 'roo-combo-removable-btn close'
17250             });
17251         }
17252
17253         if(this.hasFeedback && !this.allowBlank){
17254             
17255             inputblock.cls += ' has-feedback';
17256             
17257             inputblock.cn.push({
17258                 tag: 'span',
17259                 cls: 'glyphicon form-control-feedback'
17260             });
17261             
17262         }
17263         
17264         if (this.after) {
17265             
17266             inputblock.cls += (this.before) ? '' : ' input-group';
17267             
17268             inputblock.cn.push({
17269                 tag :'span',
17270                 cls : 'input-group-addon input-group-append input-group-text',
17271                 html : this.after
17272             });
17273         }
17274
17275         
17276         var ibwrap = inputblock;
17277         
17278         if(this.multiple){
17279             ibwrap = {
17280                 tag: 'ul',
17281                 cls: 'roo-select2-choices',
17282                 cn:[
17283                     {
17284                         tag: 'li',
17285                         cls: 'roo-select2-search-field',
17286                         cn: [
17287
17288                             inputblock
17289                         ]
17290                     }
17291                 ]
17292             };
17293         
17294             
17295         }
17296         
17297         var combobox = {
17298             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17299             cn: [
17300                 {
17301                     tag: 'input',
17302                     type : 'hidden',
17303                     cls: 'form-hidden-field'
17304                 },
17305                 ibwrap
17306             ]
17307         };
17308         
17309         if(!this.multiple && this.showToggleBtn){
17310             
17311             var caret = {
17312                 cls: 'caret'
17313             };
17314             
17315             if (this.caret != false) {
17316                 caret = {
17317                      tag: 'i',
17318                      cls: 'fa fa-' + this.caret
17319                 };
17320                 
17321             }
17322             
17323             combobox.cn.push({
17324                 tag :'span',
17325                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17326                 cn : [
17327                     Roo.bootstrap.version == 3 ? caret : '',
17328                     {
17329                         tag: 'span',
17330                         cls: 'combobox-clear',
17331                         cn  : [
17332                             {
17333                                 tag : 'i',
17334                                 cls: 'icon-remove'
17335                             }
17336                         ]
17337                     }
17338                 ]
17339
17340             })
17341         }
17342         
17343         if(this.multiple){
17344             combobox.cls += ' roo-select2-container-multi';
17345         }
17346         
17347         var align = this.labelAlign || this.parentLabelAlign();
17348         
17349         if (align ==='left' && this.fieldLabel.length) {
17350
17351             cfg.cn = [
17352                 {
17353                    tag : 'i',
17354                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17355                    tooltip : 'This field is required'
17356                 },
17357                 {
17358                     tag: 'label',
17359                     cls : 'control-label col-form-label',
17360                     html : this.fieldLabel
17361
17362                 },
17363                 {
17364                     cls : 'roo-combobox-wrap ', 
17365                     cn: [
17366                         combobox
17367                     ]
17368                 }
17369             ];
17370             
17371             var labelCfg = cfg.cn[1];
17372             var contentCfg = cfg.cn[2];
17373             
17374
17375             if(this.indicatorpos == 'right'){
17376                 cfg.cn = [
17377                     {
17378                         tag: 'label',
17379                         'for' :  id,
17380                         cls : 'control-label col-form-label',
17381                         cn : [
17382                             {
17383                                 tag : 'span',
17384                                 html : this.fieldLabel
17385                             },
17386                             {
17387                                 tag : 'i',
17388                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17389                                 tooltip : 'This field is required'
17390                             }
17391                         ]
17392                     },
17393                     {
17394                         cls : "roo-combobox-wrap ",
17395                         cn: [
17396                             combobox
17397                         ]
17398                     }
17399
17400                 ];
17401                 
17402                 labelCfg = cfg.cn[0];
17403                 contentCfg = cfg.cn[1];
17404             }
17405             
17406            
17407             
17408             if(this.labelWidth > 12){
17409                 labelCfg.style = "width: " + this.labelWidth + 'px';
17410             }
17411            
17412             if(this.labelWidth < 13 && this.labelmd == 0){
17413                 this.labelmd = this.labelWidth;
17414             }
17415             
17416             if(this.labellg > 0){
17417                 labelCfg.cls += ' col-lg-' + this.labellg;
17418                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17419             }
17420             
17421             if(this.labelmd > 0){
17422                 labelCfg.cls += ' col-md-' + this.labelmd;
17423                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17424             }
17425             
17426             if(this.labelsm > 0){
17427                 labelCfg.cls += ' col-sm-' + this.labelsm;
17428                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17429             }
17430             
17431             if(this.labelxs > 0){
17432                 labelCfg.cls += ' col-xs-' + this.labelxs;
17433                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17434             }
17435                 
17436                 
17437         } else if ( this.fieldLabel.length) {
17438             cfg.cn = [
17439                 {
17440                    tag : 'i',
17441                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17442                    tooltip : 'This field is required'
17443                 },
17444                 {
17445                     tag: 'label',
17446                     cls : 'control-label',
17447                     html : this.fieldLabel
17448
17449                 },
17450                 {
17451                     cls : '', 
17452                     cn: [
17453                         combobox
17454                     ]
17455                 }
17456             ];
17457             
17458             if(this.indicatorpos == 'right'){
17459                 cfg.cn = [
17460                     {
17461                         tag: 'label',
17462                         cls : 'control-label',
17463                         html : this.fieldLabel,
17464                         cn : [
17465                             {
17466                                tag : 'i',
17467                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17468                                tooltip : 'This field is required'
17469                             }
17470                         ]
17471                     },
17472                     {
17473                         cls : '', 
17474                         cn: [
17475                             combobox
17476                         ]
17477                     }
17478                 ];
17479             }
17480         } else {
17481             cfg.cn = combobox;    
17482         }
17483         
17484         
17485         var settings = this;
17486         
17487         ['xs','sm','md','lg'].map(function(size){
17488             if (settings[size]) {
17489                 cfg.cls += ' col-' + size + '-' + settings[size];
17490             }
17491         });
17492         
17493         return cfg;
17494     },
17495     
17496     initTouchView : function()
17497     {
17498         this.renderTouchView();
17499         
17500         this.touchViewEl.on('scroll', function(){
17501             this.el.dom.scrollTop = 0;
17502         }, this);
17503         
17504         this.originalValue = this.getValue();
17505         
17506         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17507         
17508         this.inputEl().on("click", this.showTouchView, this);
17509         if (this.triggerEl) {
17510             this.triggerEl.on("click", this.showTouchView, this);
17511         }
17512         
17513         
17514         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17515         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17516         
17517         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17518         
17519         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17520         this.store.on('load', this.onTouchViewLoad, this);
17521         this.store.on('loadexception', this.onTouchViewLoadException, this);
17522         
17523         if(this.hiddenName){
17524             
17525             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17526             
17527             this.hiddenField.dom.value =
17528                 this.hiddenValue !== undefined ? this.hiddenValue :
17529                 this.value !== undefined ? this.value : '';
17530         
17531             this.el.dom.removeAttribute('name');
17532             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17533         }
17534         
17535         if(this.multiple){
17536             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17537             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17538         }
17539         
17540         if(this.removable && !this.multiple){
17541             var close = this.closeTriggerEl();
17542             if(close){
17543                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17544                 close.on('click', this.removeBtnClick, this, close);
17545             }
17546         }
17547         /*
17548          * fix the bug in Safari iOS8
17549          */
17550         this.inputEl().on("focus", function(e){
17551             document.activeElement.blur();
17552         }, this);
17553         
17554         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17555         
17556         return;
17557         
17558         
17559     },
17560     
17561     renderTouchView : function()
17562     {
17563         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17564         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17565         
17566         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17567         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17568         
17569         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17570         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17571         this.touchViewBodyEl.setStyle('overflow', 'auto');
17572         
17573         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17574         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17575         
17576         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17577         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17578         
17579     },
17580     
17581     showTouchView : function()
17582     {
17583         if(this.disabled){
17584             return;
17585         }
17586         
17587         this.touchViewHeaderEl.hide();
17588
17589         if(this.modalTitle.length){
17590             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17591             this.touchViewHeaderEl.show();
17592         }
17593
17594         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17595         this.touchViewEl.show();
17596
17597         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17598         
17599         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17600         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17601
17602         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17603
17604         if(this.modalTitle.length){
17605             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17606         }
17607         
17608         this.touchViewBodyEl.setHeight(bodyHeight);
17609
17610         if(this.animate){
17611             var _this = this;
17612             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17613         }else{
17614             this.touchViewEl.addClass(['in','show']);
17615         }
17616         
17617         if(this._touchViewMask){
17618             Roo.get(document.body).addClass("x-body-masked");
17619             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17620             this._touchViewMask.setStyle('z-index', 10000);
17621             this._touchViewMask.addClass('show');
17622         }
17623         
17624         this.doTouchViewQuery();
17625         
17626     },
17627     
17628     hideTouchView : function()
17629     {
17630         this.touchViewEl.removeClass(['in','show']);
17631
17632         if(this.animate){
17633             var _this = this;
17634             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17635         }else{
17636             this.touchViewEl.setStyle('display', 'none');
17637         }
17638         
17639         if(this._touchViewMask){
17640             this._touchViewMask.removeClass('show');
17641             Roo.get(document.body).removeClass("x-body-masked");
17642         }
17643     },
17644     
17645     setTouchViewValue : function()
17646     {
17647         if(this.multiple){
17648             this.clearItem();
17649         
17650             var _this = this;
17651
17652             Roo.each(this.tickItems, function(o){
17653                 this.addItem(o);
17654             }, this);
17655         }
17656         
17657         this.hideTouchView();
17658     },
17659     
17660     doTouchViewQuery : function()
17661     {
17662         var qe = {
17663             query: '',
17664             forceAll: true,
17665             combo: this,
17666             cancel:false
17667         };
17668         
17669         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17670             return false;
17671         }
17672         
17673         if(!this.alwaysQuery || this.mode == 'local'){
17674             this.onTouchViewLoad();
17675             return;
17676         }
17677         
17678         this.store.load();
17679     },
17680     
17681     onTouchViewBeforeLoad : function(combo,opts)
17682     {
17683         return;
17684     },
17685
17686     // private
17687     onTouchViewLoad : function()
17688     {
17689         if(this.store.getCount() < 1){
17690             this.onTouchViewEmptyResults();
17691             return;
17692         }
17693         
17694         this.clearTouchView();
17695         
17696         var rawValue = this.getRawValue();
17697         
17698         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17699         
17700         this.tickItems = [];
17701         
17702         this.store.data.each(function(d, rowIndex){
17703             var row = this.touchViewListGroup.createChild(template);
17704             
17705             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17706                 row.addClass(d.data.cls);
17707             }
17708             
17709             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17710                 var cfg = {
17711                     data : d.data,
17712                     html : d.data[this.displayField]
17713                 };
17714                 
17715                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17716                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17717                 }
17718             }
17719             row.removeClass('selected');
17720             if(!this.multiple && this.valueField &&
17721                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17722             {
17723                 // radio buttons..
17724                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17725                 row.addClass('selected');
17726             }
17727             
17728             if(this.multiple && this.valueField &&
17729                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17730             {
17731                 
17732                 // checkboxes...
17733                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17734                 this.tickItems.push(d.data);
17735             }
17736             
17737             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17738             
17739         }, this);
17740         
17741         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17742         
17743         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17744
17745         if(this.modalTitle.length){
17746             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17747         }
17748
17749         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17750         
17751         if(this.mobile_restrict_height && listHeight < bodyHeight){
17752             this.touchViewBodyEl.setHeight(listHeight);
17753         }
17754         
17755         var _this = this;
17756         
17757         if(firstChecked && listHeight > bodyHeight){
17758             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17759         }
17760         
17761     },
17762     
17763     onTouchViewLoadException : function()
17764     {
17765         this.hideTouchView();
17766     },
17767     
17768     onTouchViewEmptyResults : function()
17769     {
17770         this.clearTouchView();
17771         
17772         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17773         
17774         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17775         
17776     },
17777     
17778     clearTouchView : function()
17779     {
17780         this.touchViewListGroup.dom.innerHTML = '';
17781     },
17782     
17783     onTouchViewClick : function(e, el, o)
17784     {
17785         e.preventDefault();
17786         
17787         var row = o.row;
17788         var rowIndex = o.rowIndex;
17789         
17790         var r = this.store.getAt(rowIndex);
17791         
17792         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17793             
17794             if(!this.multiple){
17795                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17796                     c.dom.removeAttribute('checked');
17797                 }, this);
17798
17799                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17800
17801                 this.setFromData(r.data);
17802
17803                 var close = this.closeTriggerEl();
17804
17805                 if(close){
17806                     close.show();
17807                 }
17808
17809                 this.hideTouchView();
17810
17811                 this.fireEvent('select', this, r, rowIndex);
17812
17813                 return;
17814             }
17815
17816             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17817                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17818                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17819                 return;
17820             }
17821
17822             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17823             this.addItem(r.data);
17824             this.tickItems.push(r.data);
17825         }
17826     },
17827     
17828     getAutoCreateNativeIOS : function()
17829     {
17830         var cfg = {
17831             cls: 'form-group' //input-group,
17832         };
17833         
17834         var combobox =  {
17835             tag: 'select',
17836             cls : 'roo-ios-select'
17837         };
17838         
17839         if (this.name) {
17840             combobox.name = this.name;
17841         }
17842         
17843         if (this.disabled) {
17844             combobox.disabled = true;
17845         }
17846         
17847         var settings = this;
17848         
17849         ['xs','sm','md','lg'].map(function(size){
17850             if (settings[size]) {
17851                 cfg.cls += ' col-' + size + '-' + settings[size];
17852             }
17853         });
17854         
17855         cfg.cn = combobox;
17856         
17857         return cfg;
17858         
17859     },
17860     
17861     initIOSView : function()
17862     {
17863         this.store.on('load', this.onIOSViewLoad, this);
17864         
17865         return;
17866     },
17867     
17868     onIOSViewLoad : function()
17869     {
17870         if(this.store.getCount() < 1){
17871             return;
17872         }
17873         
17874         this.clearIOSView();
17875         
17876         if(this.allowBlank) {
17877             
17878             var default_text = '-- SELECT --';
17879             
17880             if(this.placeholder.length){
17881                 default_text = this.placeholder;
17882             }
17883             
17884             if(this.emptyTitle.length){
17885                 default_text += ' - ' + this.emptyTitle + ' -';
17886             }
17887             
17888             var opt = this.inputEl().createChild({
17889                 tag: 'option',
17890                 value : 0,
17891                 html : default_text
17892             });
17893             
17894             var o = {};
17895             o[this.valueField] = 0;
17896             o[this.displayField] = default_text;
17897             
17898             this.ios_options.push({
17899                 data : o,
17900                 el : opt
17901             });
17902             
17903         }
17904         
17905         this.store.data.each(function(d, rowIndex){
17906             
17907             var html = '';
17908             
17909             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17910                 html = d.data[this.displayField];
17911             }
17912             
17913             var value = '';
17914             
17915             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17916                 value = d.data[this.valueField];
17917             }
17918             
17919             var option = {
17920                 tag: 'option',
17921                 value : value,
17922                 html : html
17923             };
17924             
17925             if(this.value == d.data[this.valueField]){
17926                 option['selected'] = true;
17927             }
17928             
17929             var opt = this.inputEl().createChild(option);
17930             
17931             this.ios_options.push({
17932                 data : d.data,
17933                 el : opt
17934             });
17935             
17936         }, this);
17937         
17938         this.inputEl().on('change', function(){
17939            this.fireEvent('select', this);
17940         }, this);
17941         
17942     },
17943     
17944     clearIOSView: function()
17945     {
17946         this.inputEl().dom.innerHTML = '';
17947         
17948         this.ios_options = [];
17949     },
17950     
17951     setIOSValue: function(v)
17952     {
17953         this.value = v;
17954         
17955         if(!this.ios_options){
17956             return;
17957         }
17958         
17959         Roo.each(this.ios_options, function(opts){
17960            
17961            opts.el.dom.removeAttribute('selected');
17962            
17963            if(opts.data[this.valueField] != v){
17964                return;
17965            }
17966            
17967            opts.el.dom.setAttribute('selected', true);
17968            
17969         }, this);
17970     }
17971
17972     /** 
17973     * @cfg {Boolean} grow 
17974     * @hide 
17975     */
17976     /** 
17977     * @cfg {Number} growMin 
17978     * @hide 
17979     */
17980     /** 
17981     * @cfg {Number} growMax 
17982     * @hide 
17983     */
17984     /**
17985      * @hide
17986      * @method autoSize
17987      */
17988 });
17989
17990 Roo.apply(Roo.bootstrap.ComboBox,  {
17991     
17992     header : {
17993         tag: 'div',
17994         cls: 'modal-header',
17995         cn: [
17996             {
17997                 tag: 'h4',
17998                 cls: 'modal-title'
17999             }
18000         ]
18001     },
18002     
18003     body : {
18004         tag: 'div',
18005         cls: 'modal-body',
18006         cn: [
18007             {
18008                 tag: 'ul',
18009                 cls: 'list-group'
18010             }
18011         ]
18012     },
18013     
18014     listItemRadio : {
18015         tag: 'li',
18016         cls: 'list-group-item',
18017         cn: [
18018             {
18019                 tag: 'span',
18020                 cls: 'roo-combobox-list-group-item-value'
18021             },
18022             {
18023                 tag: 'div',
18024                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18025                 cn: [
18026                     {
18027                         tag: 'input',
18028                         type: 'radio'
18029                     },
18030                     {
18031                         tag: 'label'
18032                     }
18033                 ]
18034             }
18035         ]
18036     },
18037     
18038     listItemCheckbox : {
18039         tag: 'li',
18040         cls: 'list-group-item',
18041         cn: [
18042             {
18043                 tag: 'span',
18044                 cls: 'roo-combobox-list-group-item-value'
18045             },
18046             {
18047                 tag: 'div',
18048                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18049                 cn: [
18050                     {
18051                         tag: 'input',
18052                         type: 'checkbox'
18053                     },
18054                     {
18055                         tag: 'label'
18056                     }
18057                 ]
18058             }
18059         ]
18060     },
18061     
18062     emptyResult : {
18063         tag: 'div',
18064         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18065     },
18066     
18067     footer : {
18068         tag: 'div',
18069         cls: 'modal-footer',
18070         cn: [
18071             {
18072                 tag: 'div',
18073                 cls: 'row',
18074                 cn: [
18075                     {
18076                         tag: 'div',
18077                         cls: 'col-xs-6 text-left',
18078                         cn: {
18079                             tag: 'button',
18080                             cls: 'btn btn-danger roo-touch-view-cancel',
18081                             html: 'Cancel'
18082                         }
18083                     },
18084                     {
18085                         tag: 'div',
18086                         cls: 'col-xs-6 text-right',
18087                         cn: {
18088                             tag: 'button',
18089                             cls: 'btn btn-success roo-touch-view-ok',
18090                             html: 'OK'
18091                         }
18092                     }
18093                 ]
18094             }
18095         ]
18096         
18097     }
18098 });
18099
18100 Roo.apply(Roo.bootstrap.ComboBox,  {
18101     
18102     touchViewTemplate : {
18103         tag: 'div',
18104         cls: 'modal fade roo-combobox-touch-view',
18105         cn: [
18106             {
18107                 tag: 'div',
18108                 cls: 'modal-dialog',
18109                 style : 'position:fixed', // we have to fix position....
18110                 cn: [
18111                     {
18112                         tag: 'div',
18113                         cls: 'modal-content',
18114                         cn: [
18115                             Roo.bootstrap.ComboBox.header,
18116                             Roo.bootstrap.ComboBox.body,
18117                             Roo.bootstrap.ComboBox.footer
18118                         ]
18119                     }
18120                 ]
18121             }
18122         ]
18123     }
18124 });/*
18125  * Based on:
18126  * Ext JS Library 1.1.1
18127  * Copyright(c) 2006-2007, Ext JS, LLC.
18128  *
18129  * Originally Released Under LGPL - original licence link has changed is not relivant.
18130  *
18131  * Fork - LGPL
18132  * <script type="text/javascript">
18133  */
18134
18135 /**
18136  * @class Roo.View
18137  * @extends Roo.util.Observable
18138  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18139  * This class also supports single and multi selection modes. <br>
18140  * Create a data model bound view:
18141  <pre><code>
18142  var store = new Roo.data.Store(...);
18143
18144  var view = new Roo.View({
18145     el : "my-element",
18146     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18147  
18148     singleSelect: true,
18149     selectedClass: "ydataview-selected",
18150     store: store
18151  });
18152
18153  // listen for node click?
18154  view.on("click", function(vw, index, node, e){
18155  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18156  });
18157
18158  // load XML data
18159  dataModel.load("foobar.xml");
18160  </code></pre>
18161  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18162  * <br><br>
18163  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18164  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18165  * 
18166  * Note: old style constructor is still suported (container, template, config)
18167  * 
18168  * @constructor
18169  * Create a new View
18170  * @param {Object} config The config object
18171  * 
18172  */
18173 Roo.View = function(config, depreciated_tpl, depreciated_config){
18174     
18175     this.parent = false;
18176     
18177     if (typeof(depreciated_tpl) == 'undefined') {
18178         // new way.. - universal constructor.
18179         Roo.apply(this, config);
18180         this.el  = Roo.get(this.el);
18181     } else {
18182         // old format..
18183         this.el  = Roo.get(config);
18184         this.tpl = depreciated_tpl;
18185         Roo.apply(this, depreciated_config);
18186     }
18187     this.wrapEl  = this.el.wrap().wrap();
18188     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18189     
18190     
18191     if(typeof(this.tpl) == "string"){
18192         this.tpl = new Roo.Template(this.tpl);
18193     } else {
18194         // support xtype ctors..
18195         this.tpl = new Roo.factory(this.tpl, Roo);
18196     }
18197     
18198     
18199     this.tpl.compile();
18200     
18201     /** @private */
18202     this.addEvents({
18203         /**
18204          * @event beforeclick
18205          * Fires before a click is processed. Returns false to cancel the default action.
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             "beforeclick" : true,
18212         /**
18213          * @event click
18214          * Fires when a template node is 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             "click" : true,
18221         /**
18222          * @event dblclick
18223          * Fires when a template node is double 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             "dblclick" : true,
18230         /**
18231          * @event contextmenu
18232          * Fires when a template node is right clicked.
18233          * @param {Roo.View} this
18234          * @param {Number} index The index of the target node
18235          * @param {HTMLElement} node The target node
18236          * @param {Roo.EventObject} e The raw event object
18237          */
18238             "contextmenu" : true,
18239         /**
18240          * @event selectionchange
18241          * Fires when the selected nodes change.
18242          * @param {Roo.View} this
18243          * @param {Array} selections Array of the selected nodes
18244          */
18245             "selectionchange" : true,
18246     
18247         /**
18248          * @event beforeselect
18249          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18250          * @param {Roo.View} this
18251          * @param {HTMLElement} node The node to be selected
18252          * @param {Array} selections Array of currently selected nodes
18253          */
18254             "beforeselect" : true,
18255         /**
18256          * @event preparedata
18257          * Fires on every row to render, to allow you to change the data.
18258          * @param {Roo.View} this
18259          * @param {Object} data to be rendered (change this)
18260          */
18261           "preparedata" : true
18262           
18263           
18264         });
18265
18266
18267
18268     this.el.on({
18269         "click": this.onClick,
18270         "dblclick": this.onDblClick,
18271         "contextmenu": this.onContextMenu,
18272         scope:this
18273     });
18274
18275     this.selections = [];
18276     this.nodes = [];
18277     this.cmp = new Roo.CompositeElementLite([]);
18278     if(this.store){
18279         this.store = Roo.factory(this.store, Roo.data);
18280         this.setStore(this.store, true);
18281     }
18282     
18283     if ( this.footer && this.footer.xtype) {
18284            
18285          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18286         
18287         this.footer.dataSource = this.store;
18288         this.footer.container = fctr;
18289         this.footer = Roo.factory(this.footer, Roo);
18290         fctr.insertFirst(this.el);
18291         
18292         // this is a bit insane - as the paging toolbar seems to detach the el..
18293 //        dom.parentNode.parentNode.parentNode
18294          // they get detached?
18295     }
18296     
18297     
18298     Roo.View.superclass.constructor.call(this);
18299     
18300     
18301 };
18302
18303 Roo.extend(Roo.View, Roo.util.Observable, {
18304     
18305      /**
18306      * @cfg {Roo.data.Store} store Data store to load data from.
18307      */
18308     store : false,
18309     
18310     /**
18311      * @cfg {String|Roo.Element} el The container element.
18312      */
18313     el : '',
18314     
18315     /**
18316      * @cfg {String|Roo.Template} tpl The template used by this View 
18317      */
18318     tpl : false,
18319     /**
18320      * @cfg {String} dataName the named area of the template to use as the data area
18321      *                          Works with domtemplates roo-name="name"
18322      */
18323     dataName: false,
18324     /**
18325      * @cfg {String} selectedClass The css class to add to selected nodes
18326      */
18327     selectedClass : "x-view-selected",
18328      /**
18329      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18330      */
18331     emptyText : "",
18332     
18333     /**
18334      * @cfg {String} text to display on mask (default Loading)
18335      */
18336     mask : false,
18337     /**
18338      * @cfg {Boolean} multiSelect Allow multiple selection
18339      */
18340     multiSelect : false,
18341     /**
18342      * @cfg {Boolean} singleSelect Allow single selection
18343      */
18344     singleSelect:  false,
18345     
18346     /**
18347      * @cfg {Boolean} toggleSelect - selecting 
18348      */
18349     toggleSelect : false,
18350     
18351     /**
18352      * @cfg {Boolean} tickable - selecting 
18353      */
18354     tickable : false,
18355     
18356     /**
18357      * Returns the element this view is bound to.
18358      * @return {Roo.Element}
18359      */
18360     getEl : function(){
18361         return this.wrapEl;
18362     },
18363     
18364     
18365
18366     /**
18367      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18368      */
18369     refresh : function(){
18370         //Roo.log('refresh');
18371         var t = this.tpl;
18372         
18373         // if we are using something like 'domtemplate', then
18374         // the what gets used is:
18375         // t.applySubtemplate(NAME, data, wrapping data..)
18376         // the outer template then get' applied with
18377         //     the store 'extra data'
18378         // and the body get's added to the
18379         //      roo-name="data" node?
18380         //      <span class='roo-tpl-{name}'></span> ?????
18381         
18382         
18383         
18384         this.clearSelections();
18385         this.el.update("");
18386         var html = [];
18387         var records = this.store.getRange();
18388         if(records.length < 1) {
18389             
18390             // is this valid??  = should it render a template??
18391             
18392             this.el.update(this.emptyText);
18393             return;
18394         }
18395         var el = this.el;
18396         if (this.dataName) {
18397             this.el.update(t.apply(this.store.meta)); //????
18398             el = this.el.child('.roo-tpl-' + this.dataName);
18399         }
18400         
18401         for(var i = 0, len = records.length; i < len; i++){
18402             var data = this.prepareData(records[i].data, i, records[i]);
18403             this.fireEvent("preparedata", this, data, i, records[i]);
18404             
18405             var d = Roo.apply({}, data);
18406             
18407             if(this.tickable){
18408                 Roo.apply(d, {'roo-id' : Roo.id()});
18409                 
18410                 var _this = this;
18411             
18412                 Roo.each(this.parent.item, function(item){
18413                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18414                         return;
18415                     }
18416                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18417                 });
18418             }
18419             
18420             html[html.length] = Roo.util.Format.trim(
18421                 this.dataName ?
18422                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18423                     t.apply(d)
18424             );
18425         }
18426         
18427         
18428         
18429         el.update(html.join(""));
18430         this.nodes = el.dom.childNodes;
18431         this.updateIndexes(0);
18432     },
18433     
18434
18435     /**
18436      * Function to override to reformat the data that is sent to
18437      * the template for each node.
18438      * DEPRICATED - use the preparedata event handler.
18439      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18440      * a JSON object for an UpdateManager bound view).
18441      */
18442     prepareData : function(data, index, record)
18443     {
18444         this.fireEvent("preparedata", this, data, index, record);
18445         return data;
18446     },
18447
18448     onUpdate : function(ds, record){
18449         // Roo.log('on update');   
18450         this.clearSelections();
18451         var index = this.store.indexOf(record);
18452         var n = this.nodes[index];
18453         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18454         n.parentNode.removeChild(n);
18455         this.updateIndexes(index, index);
18456     },
18457
18458     
18459     
18460 // --------- FIXME     
18461     onAdd : function(ds, records, index)
18462     {
18463         //Roo.log(['on Add', ds, records, index] );        
18464         this.clearSelections();
18465         if(this.nodes.length == 0){
18466             this.refresh();
18467             return;
18468         }
18469         var n = this.nodes[index];
18470         for(var i = 0, len = records.length; i < len; i++){
18471             var d = this.prepareData(records[i].data, i, records[i]);
18472             if(n){
18473                 this.tpl.insertBefore(n, d);
18474             }else{
18475                 
18476                 this.tpl.append(this.el, d);
18477             }
18478         }
18479         this.updateIndexes(index);
18480     },
18481
18482     onRemove : function(ds, record, index){
18483        // Roo.log('onRemove');
18484         this.clearSelections();
18485         var el = this.dataName  ?
18486             this.el.child('.roo-tpl-' + this.dataName) :
18487             this.el; 
18488         
18489         el.dom.removeChild(this.nodes[index]);
18490         this.updateIndexes(index);
18491     },
18492
18493     /**
18494      * Refresh an individual node.
18495      * @param {Number} index
18496      */
18497     refreshNode : function(index){
18498         this.onUpdate(this.store, this.store.getAt(index));
18499     },
18500
18501     updateIndexes : function(startIndex, endIndex){
18502         var ns = this.nodes;
18503         startIndex = startIndex || 0;
18504         endIndex = endIndex || ns.length - 1;
18505         for(var i = startIndex; i <= endIndex; i++){
18506             ns[i].nodeIndex = i;
18507         }
18508     },
18509
18510     /**
18511      * Changes the data store this view uses and refresh the view.
18512      * @param {Store} store
18513      */
18514     setStore : function(store, initial){
18515         if(!initial && this.store){
18516             this.store.un("datachanged", this.refresh);
18517             this.store.un("add", this.onAdd);
18518             this.store.un("remove", this.onRemove);
18519             this.store.un("update", this.onUpdate);
18520             this.store.un("clear", this.refresh);
18521             this.store.un("beforeload", this.onBeforeLoad);
18522             this.store.un("load", this.onLoad);
18523             this.store.un("loadexception", this.onLoad);
18524         }
18525         if(store){
18526           
18527             store.on("datachanged", this.refresh, this);
18528             store.on("add", this.onAdd, this);
18529             store.on("remove", this.onRemove, this);
18530             store.on("update", this.onUpdate, this);
18531             store.on("clear", this.refresh, this);
18532             store.on("beforeload", this.onBeforeLoad, this);
18533             store.on("load", this.onLoad, this);
18534             store.on("loadexception", this.onLoad, this);
18535         }
18536         
18537         if(store){
18538             this.refresh();
18539         }
18540     },
18541     /**
18542      * onbeforeLoad - masks the loading area.
18543      *
18544      */
18545     onBeforeLoad : function(store,opts)
18546     {
18547          //Roo.log('onBeforeLoad');   
18548         if (!opts.add) {
18549             this.el.update("");
18550         }
18551         this.el.mask(this.mask ? this.mask : "Loading" ); 
18552     },
18553     onLoad : function ()
18554     {
18555         this.el.unmask();
18556     },
18557     
18558
18559     /**
18560      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18561      * @param {HTMLElement} node
18562      * @return {HTMLElement} The template node
18563      */
18564     findItemFromChild : function(node){
18565         var el = this.dataName  ?
18566             this.el.child('.roo-tpl-' + this.dataName,true) :
18567             this.el.dom; 
18568         
18569         if(!node || node.parentNode == el){
18570                     return node;
18571             }
18572             var p = node.parentNode;
18573             while(p && p != el){
18574             if(p.parentNode == el){
18575                 return p;
18576             }
18577             p = p.parentNode;
18578         }
18579             return null;
18580     },
18581
18582     /** @ignore */
18583     onClick : function(e){
18584         var item = this.findItemFromChild(e.getTarget());
18585         if(item){
18586             var index = this.indexOf(item);
18587             if(this.onItemClick(item, index, e) !== false){
18588                 this.fireEvent("click", this, index, item, e);
18589             }
18590         }else{
18591             this.clearSelections();
18592         }
18593     },
18594
18595     /** @ignore */
18596     onContextMenu : function(e){
18597         var item = this.findItemFromChild(e.getTarget());
18598         if(item){
18599             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18600         }
18601     },
18602
18603     /** @ignore */
18604     onDblClick : function(e){
18605         var item = this.findItemFromChild(e.getTarget());
18606         if(item){
18607             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18608         }
18609     },
18610
18611     onItemClick : function(item, index, e)
18612     {
18613         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18614             return false;
18615         }
18616         if (this.toggleSelect) {
18617             var m = this.isSelected(item) ? 'unselect' : 'select';
18618             //Roo.log(m);
18619             var _t = this;
18620             _t[m](item, true, false);
18621             return true;
18622         }
18623         if(this.multiSelect || this.singleSelect){
18624             if(this.multiSelect && e.shiftKey && this.lastSelection){
18625                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18626             }else{
18627                 this.select(item, this.multiSelect && e.ctrlKey);
18628                 this.lastSelection = item;
18629             }
18630             
18631             if(!this.tickable){
18632                 e.preventDefault();
18633             }
18634             
18635         }
18636         return true;
18637     },
18638
18639     /**
18640      * Get the number of selected nodes.
18641      * @return {Number}
18642      */
18643     getSelectionCount : function(){
18644         return this.selections.length;
18645     },
18646
18647     /**
18648      * Get the currently selected nodes.
18649      * @return {Array} An array of HTMLElements
18650      */
18651     getSelectedNodes : function(){
18652         return this.selections;
18653     },
18654
18655     /**
18656      * Get the indexes of the selected nodes.
18657      * @return {Array}
18658      */
18659     getSelectedIndexes : function(){
18660         var indexes = [], s = this.selections;
18661         for(var i = 0, len = s.length; i < len; i++){
18662             indexes.push(s[i].nodeIndex);
18663         }
18664         return indexes;
18665     },
18666
18667     /**
18668      * Clear all selections
18669      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18670      */
18671     clearSelections : function(suppressEvent){
18672         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18673             this.cmp.elements = this.selections;
18674             this.cmp.removeClass(this.selectedClass);
18675             this.selections = [];
18676             if(!suppressEvent){
18677                 this.fireEvent("selectionchange", this, this.selections);
18678             }
18679         }
18680     },
18681
18682     /**
18683      * Returns true if the passed node is selected
18684      * @param {HTMLElement/Number} node The node or node index
18685      * @return {Boolean}
18686      */
18687     isSelected : function(node){
18688         var s = this.selections;
18689         if(s.length < 1){
18690             return false;
18691         }
18692         node = this.getNode(node);
18693         return s.indexOf(node) !== -1;
18694     },
18695
18696     /**
18697      * Selects nodes.
18698      * @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
18699      * @param {Boolean} keepExisting (optional) true to keep existing selections
18700      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18701      */
18702     select : function(nodeInfo, keepExisting, suppressEvent){
18703         if(nodeInfo instanceof Array){
18704             if(!keepExisting){
18705                 this.clearSelections(true);
18706             }
18707             for(var i = 0, len = nodeInfo.length; i < len; i++){
18708                 this.select(nodeInfo[i], true, true);
18709             }
18710             return;
18711         } 
18712         var node = this.getNode(nodeInfo);
18713         if(!node || this.isSelected(node)){
18714             return; // already selected.
18715         }
18716         if(!keepExisting){
18717             this.clearSelections(true);
18718         }
18719         
18720         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18721             Roo.fly(node).addClass(this.selectedClass);
18722             this.selections.push(node);
18723             if(!suppressEvent){
18724                 this.fireEvent("selectionchange", this, this.selections);
18725             }
18726         }
18727         
18728         
18729     },
18730       /**
18731      * Unselects nodes.
18732      * @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
18733      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18734      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18735      */
18736     unselect : function(nodeInfo, keepExisting, suppressEvent)
18737     {
18738         if(nodeInfo instanceof Array){
18739             Roo.each(this.selections, function(s) {
18740                 this.unselect(s, nodeInfo);
18741             }, this);
18742             return;
18743         }
18744         var node = this.getNode(nodeInfo);
18745         if(!node || !this.isSelected(node)){
18746             //Roo.log("not selected");
18747             return; // not selected.
18748         }
18749         // fireevent???
18750         var ns = [];
18751         Roo.each(this.selections, function(s) {
18752             if (s == node ) {
18753                 Roo.fly(node).removeClass(this.selectedClass);
18754
18755                 return;
18756             }
18757             ns.push(s);
18758         },this);
18759         
18760         this.selections= ns;
18761         this.fireEvent("selectionchange", this, this.selections);
18762     },
18763
18764     /**
18765      * Gets a template node.
18766      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18767      * @return {HTMLElement} The node or null if it wasn't found
18768      */
18769     getNode : function(nodeInfo){
18770         if(typeof nodeInfo == "string"){
18771             return document.getElementById(nodeInfo);
18772         }else if(typeof nodeInfo == "number"){
18773             return this.nodes[nodeInfo];
18774         }
18775         return nodeInfo;
18776     },
18777
18778     /**
18779      * Gets a range template nodes.
18780      * @param {Number} startIndex
18781      * @param {Number} endIndex
18782      * @return {Array} An array of nodes
18783      */
18784     getNodes : function(start, end){
18785         var ns = this.nodes;
18786         start = start || 0;
18787         end = typeof end == "undefined" ? ns.length - 1 : end;
18788         var nodes = [];
18789         if(start <= end){
18790             for(var i = start; i <= end; i++){
18791                 nodes.push(ns[i]);
18792             }
18793         } else{
18794             for(var i = start; i >= end; i--){
18795                 nodes.push(ns[i]);
18796             }
18797         }
18798         return nodes;
18799     },
18800
18801     /**
18802      * Finds the index of the passed node
18803      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18804      * @return {Number} The index of the node or -1
18805      */
18806     indexOf : function(node){
18807         node = this.getNode(node);
18808         if(typeof node.nodeIndex == "number"){
18809             return node.nodeIndex;
18810         }
18811         var ns = this.nodes;
18812         for(var i = 0, len = ns.length; i < len; i++){
18813             if(ns[i] == node){
18814                 return i;
18815             }
18816         }
18817         return -1;
18818     }
18819 });
18820 /*
18821  * - LGPL
18822  *
18823  * based on jquery fullcalendar
18824  * 
18825  */
18826
18827 Roo.bootstrap = Roo.bootstrap || {};
18828 /**
18829  * @class Roo.bootstrap.Calendar
18830  * @extends Roo.bootstrap.Component
18831  * Bootstrap Calendar class
18832  * @cfg {Boolean} loadMask (true|false) default false
18833  * @cfg {Object} header generate the user specific header of the calendar, default false
18834
18835  * @constructor
18836  * Create a new Container
18837  * @param {Object} config The config object
18838  */
18839
18840
18841
18842 Roo.bootstrap.Calendar = function(config){
18843     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18844      this.addEvents({
18845         /**
18846              * @event select
18847              * Fires when a date is selected
18848              * @param {DatePicker} this
18849              * @param {Date} date The selected date
18850              */
18851         'select': true,
18852         /**
18853              * @event monthchange
18854              * Fires when the displayed month changes 
18855              * @param {DatePicker} this
18856              * @param {Date} date The selected month
18857              */
18858         'monthchange': true,
18859         /**
18860              * @event evententer
18861              * Fires when mouse over an event
18862              * @param {Calendar} this
18863              * @param {event} Event
18864              */
18865         'evententer': true,
18866         /**
18867              * @event eventleave
18868              * Fires when the mouse leaves an
18869              * @param {Calendar} this
18870              * @param {event}
18871              */
18872         'eventleave': true,
18873         /**
18874              * @event eventclick
18875              * Fires when the mouse click an
18876              * @param {Calendar} this
18877              * @param {event}
18878              */
18879         'eventclick': true
18880         
18881     });
18882
18883 };
18884
18885 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18886     
18887      /**
18888      * @cfg {Number} startDay
18889      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18890      */
18891     startDay : 0,
18892     
18893     loadMask : false,
18894     
18895     header : false,
18896       
18897     getAutoCreate : function(){
18898         
18899         
18900         var fc_button = function(name, corner, style, content ) {
18901             return Roo.apply({},{
18902                 tag : 'span',
18903                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18904                          (corner.length ?
18905                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18906                             ''
18907                         ),
18908                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18909                 unselectable: 'on'
18910             });
18911         };
18912         
18913         var header = {};
18914         
18915         if(!this.header){
18916             header = {
18917                 tag : 'table',
18918                 cls : 'fc-header',
18919                 style : 'width:100%',
18920                 cn : [
18921                     {
18922                         tag: 'tr',
18923                         cn : [
18924                             {
18925                                 tag : 'td',
18926                                 cls : 'fc-header-left',
18927                                 cn : [
18928                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18929                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18930                                     { tag: 'span', cls: 'fc-header-space' },
18931                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18932
18933
18934                                 ]
18935                             },
18936
18937                             {
18938                                 tag : 'td',
18939                                 cls : 'fc-header-center',
18940                                 cn : [
18941                                     {
18942                                         tag: 'span',
18943                                         cls: 'fc-header-title',
18944                                         cn : {
18945                                             tag: 'H2',
18946                                             html : 'month / year'
18947                                         }
18948                                     }
18949
18950                                 ]
18951                             },
18952                             {
18953                                 tag : 'td',
18954                                 cls : 'fc-header-right',
18955                                 cn : [
18956                               /*      fc_button('month', 'left', '', 'month' ),
18957                                     fc_button('week', '', '', 'week' ),
18958                                     fc_button('day', 'right', '', 'day' )
18959                                 */    
18960
18961                                 ]
18962                             }
18963
18964                         ]
18965                     }
18966                 ]
18967             };
18968         }
18969         
18970         header = this.header;
18971         
18972        
18973         var cal_heads = function() {
18974             var ret = [];
18975             // fixme - handle this.
18976             
18977             for (var i =0; i < Date.dayNames.length; i++) {
18978                 var d = Date.dayNames[i];
18979                 ret.push({
18980                     tag: 'th',
18981                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18982                     html : d.substring(0,3)
18983                 });
18984                 
18985             }
18986             ret[0].cls += ' fc-first';
18987             ret[6].cls += ' fc-last';
18988             return ret;
18989         };
18990         var cal_cell = function(n) {
18991             return  {
18992                 tag: 'td',
18993                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18994                 cn : [
18995                     {
18996                         cn : [
18997                             {
18998                                 cls: 'fc-day-number',
18999                                 html: 'D'
19000                             },
19001                             {
19002                                 cls: 'fc-day-content',
19003                              
19004                                 cn : [
19005                                      {
19006                                         style: 'position: relative;' // height: 17px;
19007                                     }
19008                                 ]
19009                             }
19010                             
19011                             
19012                         ]
19013                     }
19014                 ]
19015                 
19016             }
19017         };
19018         var cal_rows = function() {
19019             
19020             var ret = [];
19021             for (var r = 0; r < 6; r++) {
19022                 var row= {
19023                     tag : 'tr',
19024                     cls : 'fc-week',
19025                     cn : []
19026                 };
19027                 
19028                 for (var i =0; i < Date.dayNames.length; i++) {
19029                     var d = Date.dayNames[i];
19030                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19031
19032                 }
19033                 row.cn[0].cls+=' fc-first';
19034                 row.cn[0].cn[0].style = 'min-height:90px';
19035                 row.cn[6].cls+=' fc-last';
19036                 ret.push(row);
19037                 
19038             }
19039             ret[0].cls += ' fc-first';
19040             ret[4].cls += ' fc-prev-last';
19041             ret[5].cls += ' fc-last';
19042             return ret;
19043             
19044         };
19045         
19046         var cal_table = {
19047             tag: 'table',
19048             cls: 'fc-border-separate',
19049             style : 'width:100%',
19050             cellspacing  : 0,
19051             cn : [
19052                 { 
19053                     tag: 'thead',
19054                     cn : [
19055                         { 
19056                             tag: 'tr',
19057                             cls : 'fc-first fc-last',
19058                             cn : cal_heads()
19059                         }
19060                     ]
19061                 },
19062                 { 
19063                     tag: 'tbody',
19064                     cn : cal_rows()
19065                 }
19066                   
19067             ]
19068         };
19069          
19070          var cfg = {
19071             cls : 'fc fc-ltr',
19072             cn : [
19073                 header,
19074                 {
19075                     cls : 'fc-content',
19076                     style : "position: relative;",
19077                     cn : [
19078                         {
19079                             cls : 'fc-view fc-view-month fc-grid',
19080                             style : 'position: relative',
19081                             unselectable : 'on',
19082                             cn : [
19083                                 {
19084                                     cls : 'fc-event-container',
19085                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19086                                 },
19087                                 cal_table
19088                             ]
19089                         }
19090                     ]
19091     
19092                 }
19093            ] 
19094             
19095         };
19096         
19097          
19098         
19099         return cfg;
19100     },
19101     
19102     
19103     initEvents : function()
19104     {
19105         if(!this.store){
19106             throw "can not find store for calendar";
19107         }
19108         
19109         var mark = {
19110             tag: "div",
19111             cls:"x-dlg-mask",
19112             style: "text-align:center",
19113             cn: [
19114                 {
19115                     tag: "div",
19116                     style: "background-color:white;width:50%;margin:250 auto",
19117                     cn: [
19118                         {
19119                             tag: "img",
19120                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19121                         },
19122                         {
19123                             tag: "span",
19124                             html: "Loading"
19125                         }
19126                         
19127                     ]
19128                 }
19129             ]
19130         };
19131         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19132         
19133         var size = this.el.select('.fc-content', true).first().getSize();
19134         this.maskEl.setSize(size.width, size.height);
19135         this.maskEl.enableDisplayMode("block");
19136         if(!this.loadMask){
19137             this.maskEl.hide();
19138         }
19139         
19140         this.store = Roo.factory(this.store, Roo.data);
19141         this.store.on('load', this.onLoad, this);
19142         this.store.on('beforeload', this.onBeforeLoad, this);
19143         
19144         this.resize();
19145         
19146         this.cells = this.el.select('.fc-day',true);
19147         //Roo.log(this.cells);
19148         this.textNodes = this.el.query('.fc-day-number');
19149         this.cells.addClassOnOver('fc-state-hover');
19150         
19151         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19152         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19153         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19154         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19155         
19156         this.on('monthchange', this.onMonthChange, this);
19157         
19158         this.update(new Date().clearTime());
19159     },
19160     
19161     resize : function() {
19162         var sz  = this.el.getSize();
19163         
19164         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19165         this.el.select('.fc-day-content div',true).setHeight(34);
19166     },
19167     
19168     
19169     // private
19170     showPrevMonth : function(e){
19171         this.update(this.activeDate.add("mo", -1));
19172     },
19173     showToday : function(e){
19174         this.update(new Date().clearTime());
19175     },
19176     // private
19177     showNextMonth : function(e){
19178         this.update(this.activeDate.add("mo", 1));
19179     },
19180
19181     // private
19182     showPrevYear : function(){
19183         this.update(this.activeDate.add("y", -1));
19184     },
19185
19186     // private
19187     showNextYear : function(){
19188         this.update(this.activeDate.add("y", 1));
19189     },
19190
19191     
19192    // private
19193     update : function(date)
19194     {
19195         var vd = this.activeDate;
19196         this.activeDate = date;
19197 //        if(vd && this.el){
19198 //            var t = date.getTime();
19199 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19200 //                Roo.log('using add remove');
19201 //                
19202 //                this.fireEvent('monthchange', this, date);
19203 //                
19204 //                this.cells.removeClass("fc-state-highlight");
19205 //                this.cells.each(function(c){
19206 //                   if(c.dateValue == t){
19207 //                       c.addClass("fc-state-highlight");
19208 //                       setTimeout(function(){
19209 //                            try{c.dom.firstChild.focus();}catch(e){}
19210 //                       }, 50);
19211 //                       return false;
19212 //                   }
19213 //                   return true;
19214 //                });
19215 //                return;
19216 //            }
19217 //        }
19218         
19219         var days = date.getDaysInMonth();
19220         
19221         var firstOfMonth = date.getFirstDateOfMonth();
19222         var startingPos = firstOfMonth.getDay()-this.startDay;
19223         
19224         if(startingPos < this.startDay){
19225             startingPos += 7;
19226         }
19227         
19228         var pm = date.add(Date.MONTH, -1);
19229         var prevStart = pm.getDaysInMonth()-startingPos;
19230 //        
19231         this.cells = this.el.select('.fc-day',true);
19232         this.textNodes = this.el.query('.fc-day-number');
19233         this.cells.addClassOnOver('fc-state-hover');
19234         
19235         var cells = this.cells.elements;
19236         var textEls = this.textNodes;
19237         
19238         Roo.each(cells, function(cell){
19239             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19240         });
19241         
19242         days += startingPos;
19243
19244         // convert everything to numbers so it's fast
19245         var day = 86400000;
19246         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19247         //Roo.log(d);
19248         //Roo.log(pm);
19249         //Roo.log(prevStart);
19250         
19251         var today = new Date().clearTime().getTime();
19252         var sel = date.clearTime().getTime();
19253         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19254         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19255         var ddMatch = this.disabledDatesRE;
19256         var ddText = this.disabledDatesText;
19257         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19258         var ddaysText = this.disabledDaysText;
19259         var format = this.format;
19260         
19261         var setCellClass = function(cal, cell){
19262             cell.row = 0;
19263             cell.events = [];
19264             cell.more = [];
19265             //Roo.log('set Cell Class');
19266             cell.title = "";
19267             var t = d.getTime();
19268             
19269             //Roo.log(d);
19270             
19271             cell.dateValue = t;
19272             if(t == today){
19273                 cell.className += " fc-today";
19274                 cell.className += " fc-state-highlight";
19275                 cell.title = cal.todayText;
19276             }
19277             if(t == sel){
19278                 // disable highlight in other month..
19279                 //cell.className += " fc-state-highlight";
19280                 
19281             }
19282             // disabling
19283             if(t < min) {
19284                 cell.className = " fc-state-disabled";
19285                 cell.title = cal.minText;
19286                 return;
19287             }
19288             if(t > max) {
19289                 cell.className = " fc-state-disabled";
19290                 cell.title = cal.maxText;
19291                 return;
19292             }
19293             if(ddays){
19294                 if(ddays.indexOf(d.getDay()) != -1){
19295                     cell.title = ddaysText;
19296                     cell.className = " fc-state-disabled";
19297                 }
19298             }
19299             if(ddMatch && format){
19300                 var fvalue = d.dateFormat(format);
19301                 if(ddMatch.test(fvalue)){
19302                     cell.title = ddText.replace("%0", fvalue);
19303                     cell.className = " fc-state-disabled";
19304                 }
19305             }
19306             
19307             if (!cell.initialClassName) {
19308                 cell.initialClassName = cell.dom.className;
19309             }
19310             
19311             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19312         };
19313
19314         var i = 0;
19315         
19316         for(; i < startingPos; i++) {
19317             textEls[i].innerHTML = (++prevStart);
19318             d.setDate(d.getDate()+1);
19319             
19320             cells[i].className = "fc-past fc-other-month";
19321             setCellClass(this, cells[i]);
19322         }
19323         
19324         var intDay = 0;
19325         
19326         for(; i < days; i++){
19327             intDay = i - startingPos + 1;
19328             textEls[i].innerHTML = (intDay);
19329             d.setDate(d.getDate()+1);
19330             
19331             cells[i].className = ''; // "x-date-active";
19332             setCellClass(this, cells[i]);
19333         }
19334         var extraDays = 0;
19335         
19336         for(; i < 42; i++) {
19337             textEls[i].innerHTML = (++extraDays);
19338             d.setDate(d.getDate()+1);
19339             
19340             cells[i].className = "fc-future fc-other-month";
19341             setCellClass(this, cells[i]);
19342         }
19343         
19344         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19345         
19346         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19347         
19348         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19349         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19350         
19351         if(totalRows != 6){
19352             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19353             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19354         }
19355         
19356         this.fireEvent('monthchange', this, date);
19357         
19358         
19359         /*
19360         if(!this.internalRender){
19361             var main = this.el.dom.firstChild;
19362             var w = main.offsetWidth;
19363             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19364             Roo.fly(main).setWidth(w);
19365             this.internalRender = true;
19366             // opera does not respect the auto grow header center column
19367             // then, after it gets a width opera refuses to recalculate
19368             // without a second pass
19369             if(Roo.isOpera && !this.secondPass){
19370                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19371                 this.secondPass = true;
19372                 this.update.defer(10, this, [date]);
19373             }
19374         }
19375         */
19376         
19377     },
19378     
19379     findCell : function(dt) {
19380         dt = dt.clearTime().getTime();
19381         var ret = false;
19382         this.cells.each(function(c){
19383             //Roo.log("check " +c.dateValue + '?=' + dt);
19384             if(c.dateValue == dt){
19385                 ret = c;
19386                 return false;
19387             }
19388             return true;
19389         });
19390         
19391         return ret;
19392     },
19393     
19394     findCells : function(ev) {
19395         var s = ev.start.clone().clearTime().getTime();
19396        // Roo.log(s);
19397         var e= ev.end.clone().clearTime().getTime();
19398        // Roo.log(e);
19399         var ret = [];
19400         this.cells.each(function(c){
19401              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19402             
19403             if(c.dateValue > e){
19404                 return ;
19405             }
19406             if(c.dateValue < s){
19407                 return ;
19408             }
19409             ret.push(c);
19410         });
19411         
19412         return ret;    
19413     },
19414     
19415 //    findBestRow: function(cells)
19416 //    {
19417 //        var ret = 0;
19418 //        
19419 //        for (var i =0 ; i < cells.length;i++) {
19420 //            ret  = Math.max(cells[i].rows || 0,ret);
19421 //        }
19422 //        return ret;
19423 //        
19424 //    },
19425     
19426     
19427     addItem : function(ev)
19428     {
19429         // look for vertical location slot in
19430         var cells = this.findCells(ev);
19431         
19432 //        ev.row = this.findBestRow(cells);
19433         
19434         // work out the location.
19435         
19436         var crow = false;
19437         var rows = [];
19438         for(var i =0; i < cells.length; i++) {
19439             
19440             cells[i].row = cells[0].row;
19441             
19442             if(i == 0){
19443                 cells[i].row = cells[i].row + 1;
19444             }
19445             
19446             if (!crow) {
19447                 crow = {
19448                     start : cells[i],
19449                     end :  cells[i]
19450                 };
19451                 continue;
19452             }
19453             if (crow.start.getY() == cells[i].getY()) {
19454                 // on same row.
19455                 crow.end = cells[i];
19456                 continue;
19457             }
19458             // different row.
19459             rows.push(crow);
19460             crow = {
19461                 start: cells[i],
19462                 end : cells[i]
19463             };
19464             
19465         }
19466         
19467         rows.push(crow);
19468         ev.els = [];
19469         ev.rows = rows;
19470         ev.cells = cells;
19471         
19472         cells[0].events.push(ev);
19473         
19474         this.calevents.push(ev);
19475     },
19476     
19477     clearEvents: function() {
19478         
19479         if(!this.calevents){
19480             return;
19481         }
19482         
19483         Roo.each(this.cells.elements, function(c){
19484             c.row = 0;
19485             c.events = [];
19486             c.more = [];
19487         });
19488         
19489         Roo.each(this.calevents, function(e) {
19490             Roo.each(e.els, function(el) {
19491                 el.un('mouseenter' ,this.onEventEnter, this);
19492                 el.un('mouseleave' ,this.onEventLeave, this);
19493                 el.remove();
19494             },this);
19495         },this);
19496         
19497         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19498             e.remove();
19499         });
19500         
19501     },
19502     
19503     renderEvents: function()
19504     {   
19505         var _this = this;
19506         
19507         this.cells.each(function(c) {
19508             
19509             if(c.row < 5){
19510                 return;
19511             }
19512             
19513             var ev = c.events;
19514             
19515             var r = 4;
19516             if(c.row != c.events.length){
19517                 r = 4 - (4 - (c.row - c.events.length));
19518             }
19519             
19520             c.events = ev.slice(0, r);
19521             c.more = ev.slice(r);
19522             
19523             if(c.more.length && c.more.length == 1){
19524                 c.events.push(c.more.pop());
19525             }
19526             
19527             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19528             
19529         });
19530             
19531         this.cells.each(function(c) {
19532             
19533             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19534             
19535             
19536             for (var e = 0; e < c.events.length; e++){
19537                 var ev = c.events[e];
19538                 var rows = ev.rows;
19539                 
19540                 for(var i = 0; i < rows.length; i++) {
19541                 
19542                     // how many rows should it span..
19543
19544                     var  cfg = {
19545                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19546                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19547
19548                         unselectable : "on",
19549                         cn : [
19550                             {
19551                                 cls: 'fc-event-inner',
19552                                 cn : [
19553     //                                {
19554     //                                  tag:'span',
19555     //                                  cls: 'fc-event-time',
19556     //                                  html : cells.length > 1 ? '' : ev.time
19557     //                                },
19558                                     {
19559                                       tag:'span',
19560                                       cls: 'fc-event-title',
19561                                       html : String.format('{0}', ev.title)
19562                                     }
19563
19564
19565                                 ]
19566                             },
19567                             {
19568                                 cls: 'ui-resizable-handle ui-resizable-e',
19569                                 html : '&nbsp;&nbsp;&nbsp'
19570                             }
19571
19572                         ]
19573                     };
19574
19575                     if (i == 0) {
19576                         cfg.cls += ' fc-event-start';
19577                     }
19578                     if ((i+1) == rows.length) {
19579                         cfg.cls += ' fc-event-end';
19580                     }
19581
19582                     var ctr = _this.el.select('.fc-event-container',true).first();
19583                     var cg = ctr.createChild(cfg);
19584
19585                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19586                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19587
19588                     var r = (c.more.length) ? 1 : 0;
19589                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19590                     cg.setWidth(ebox.right - sbox.x -2);
19591
19592                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19593                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19594                     cg.on('click', _this.onEventClick, _this, ev);
19595
19596                     ev.els.push(cg);
19597                     
19598                 }
19599                 
19600             }
19601             
19602             
19603             if(c.more.length){
19604                 var  cfg = {
19605                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19606                     style : 'position: absolute',
19607                     unselectable : "on",
19608                     cn : [
19609                         {
19610                             cls: 'fc-event-inner',
19611                             cn : [
19612                                 {
19613                                   tag:'span',
19614                                   cls: 'fc-event-title',
19615                                   html : 'More'
19616                                 }
19617
19618
19619                             ]
19620                         },
19621                         {
19622                             cls: 'ui-resizable-handle ui-resizable-e',
19623                             html : '&nbsp;&nbsp;&nbsp'
19624                         }
19625
19626                     ]
19627                 };
19628
19629                 var ctr = _this.el.select('.fc-event-container',true).first();
19630                 var cg = ctr.createChild(cfg);
19631
19632                 var sbox = c.select('.fc-day-content',true).first().getBox();
19633                 var ebox = c.select('.fc-day-content',true).first().getBox();
19634                 //Roo.log(cg);
19635                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19636                 cg.setWidth(ebox.right - sbox.x -2);
19637
19638                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19639                 
19640             }
19641             
19642         });
19643         
19644         
19645         
19646     },
19647     
19648     onEventEnter: function (e, el,event,d) {
19649         this.fireEvent('evententer', this, el, event);
19650     },
19651     
19652     onEventLeave: function (e, el,event,d) {
19653         this.fireEvent('eventleave', this, el, event);
19654     },
19655     
19656     onEventClick: function (e, el,event,d) {
19657         this.fireEvent('eventclick', this, el, event);
19658     },
19659     
19660     onMonthChange: function () {
19661         this.store.load();
19662     },
19663     
19664     onMoreEventClick: function(e, el, more)
19665     {
19666         var _this = this;
19667         
19668         this.calpopover.placement = 'right';
19669         this.calpopover.setTitle('More');
19670         
19671         this.calpopover.setContent('');
19672         
19673         var ctr = this.calpopover.el.select('.popover-content', true).first();
19674         
19675         Roo.each(more, function(m){
19676             var cfg = {
19677                 cls : 'fc-event-hori fc-event-draggable',
19678                 html : m.title
19679             };
19680             var cg = ctr.createChild(cfg);
19681             
19682             cg.on('click', _this.onEventClick, _this, m);
19683         });
19684         
19685         this.calpopover.show(el);
19686         
19687         
19688     },
19689     
19690     onLoad: function () 
19691     {   
19692         this.calevents = [];
19693         var cal = this;
19694         
19695         if(this.store.getCount() > 0){
19696             this.store.data.each(function(d){
19697                cal.addItem({
19698                     id : d.data.id,
19699                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19700                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19701                     time : d.data.start_time,
19702                     title : d.data.title,
19703                     description : d.data.description,
19704                     venue : d.data.venue
19705                 });
19706             });
19707         }
19708         
19709         this.renderEvents();
19710         
19711         if(this.calevents.length && this.loadMask){
19712             this.maskEl.hide();
19713         }
19714     },
19715     
19716     onBeforeLoad: function()
19717     {
19718         this.clearEvents();
19719         if(this.loadMask){
19720             this.maskEl.show();
19721         }
19722     }
19723 });
19724
19725  
19726  /*
19727  * - LGPL
19728  *
19729  * element
19730  * 
19731  */
19732
19733 /**
19734  * @class Roo.bootstrap.Popover
19735  * @extends Roo.bootstrap.Component
19736  * Bootstrap Popover class
19737  * @cfg {String} html contents of the popover   (or false to use children..)
19738  * @cfg {String} title of popover (or false to hide)
19739  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19740  * @cfg {String} trigger click || hover (or false to trigger manually)
19741  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19742  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19743  *      - if false and it has a 'parent' then it will be automatically added to that element
19744  *      - if string - Roo.get  will be called 
19745  * @cfg {Number} delay - delay before showing
19746  
19747  * @constructor
19748  * Create a new Popover
19749  * @param {Object} config The config object
19750  */
19751
19752 Roo.bootstrap.Popover = function(config){
19753     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19754     
19755     this.addEvents({
19756         // raw events
19757          /**
19758          * @event show
19759          * After the popover show
19760          * 
19761          * @param {Roo.bootstrap.Popover} this
19762          */
19763         "show" : true,
19764         /**
19765          * @event hide
19766          * After the popover hide
19767          * 
19768          * @param {Roo.bootstrap.Popover} this
19769          */
19770         "hide" : true
19771     });
19772 };
19773
19774 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19775     
19776     title: false,
19777     html: false,
19778     
19779     placement : 'right',
19780     trigger : 'hover', // hover
19781     modal : false,
19782     delay : 0,
19783     
19784     over: false,
19785     
19786     can_build_overlaid : false,
19787     
19788     maskEl : false, // the mask element
19789     headerEl : false,
19790     contentEl : false,
19791     alignEl : false, // when show is called with an element - this get's stored.
19792     
19793     getChildContainer : function()
19794     {
19795         return this.contentEl;
19796         
19797     },
19798     getPopoverHeader : function()
19799     {
19800         this.title = true; // flag not to hide it..
19801         this.headerEl.addClass('p-0');
19802         return this.headerEl
19803     },
19804     
19805     
19806     getAutoCreate : function(){
19807          
19808         var cfg = {
19809            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19810            style: 'display:block',
19811            cn : [
19812                 {
19813                     cls : 'arrow'
19814                 },
19815                 {
19816                     cls : 'popover-inner ',
19817                     cn : [
19818                         {
19819                             tag: 'h3',
19820                             cls: 'popover-title popover-header',
19821                             html : this.title === false ? '' : this.title
19822                         },
19823                         {
19824                             cls : 'popover-content popover-body '  + (this.cls || ''),
19825                             html : this.html || ''
19826                         }
19827                     ]
19828                     
19829                 }
19830            ]
19831         };
19832         
19833         return cfg;
19834     },
19835     /**
19836      * @param {string} the title
19837      */
19838     setTitle: function(str)
19839     {
19840         this.title = str;
19841         if (this.el) {
19842             this.headerEl.dom.innerHTML = str;
19843         }
19844         
19845     },
19846     /**
19847      * @param {string} the body content
19848      */
19849     setContent: function(str)
19850     {
19851         this.html = str;
19852         if (this.contentEl) {
19853             this.contentEl.dom.innerHTML = str;
19854         }
19855         
19856     },
19857     // as it get's added to the bottom of the page.
19858     onRender : function(ct, position)
19859     {
19860         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19861         
19862         
19863         
19864         if(!this.el){
19865             var cfg = Roo.apply({},  this.getAutoCreate());
19866             cfg.id = Roo.id();
19867             
19868             if (this.cls) {
19869                 cfg.cls += ' ' + this.cls;
19870             }
19871             if (this.style) {
19872                 cfg.style = this.style;
19873             }
19874             //Roo.log("adding to ");
19875             this.el = Roo.get(document.body).createChild(cfg, position);
19876 //            Roo.log(this.el);
19877         }
19878         
19879         this.contentEl = this.el.select('.popover-content',true).first();
19880         this.headerEl =  this.el.select('.popover-title',true).first();
19881         
19882         var nitems = [];
19883         if(typeof(this.items) != 'undefined'){
19884             var items = this.items;
19885             delete this.items;
19886
19887             for(var i =0;i < items.length;i++) {
19888                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19889             }
19890         }
19891
19892         this.items = nitems;
19893         
19894         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19895         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19896         
19897         
19898         
19899         this.initEvents();
19900     },
19901     
19902     resizeMask : function()
19903     {
19904         this.maskEl.setSize(
19905             Roo.lib.Dom.getViewWidth(true),
19906             Roo.lib.Dom.getViewHeight(true)
19907         );
19908     },
19909     
19910     initEvents : function()
19911     {
19912         
19913         if (!this.modal) { 
19914             Roo.bootstrap.Popover.register(this);
19915         }
19916          
19917         this.arrowEl = this.el.select('.arrow',true).first();
19918         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19919         this.el.enableDisplayMode('block');
19920         this.el.hide();
19921  
19922         
19923         if (this.over === false && !this.parent()) {
19924             return; 
19925         }
19926         if (this.triggers === false) {
19927             return;
19928         }
19929          
19930         // support parent
19931         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19932         var triggers = this.trigger ? this.trigger.split(' ') : [];
19933         Roo.each(triggers, function(trigger) {
19934         
19935             if (trigger == 'click') {
19936                 on_el.on('click', this.toggle, this);
19937             } else if (trigger != 'manual') {
19938                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19939                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19940       
19941                 on_el.on(eventIn  ,this.enter, this);
19942                 on_el.on(eventOut, this.leave, this);
19943             }
19944         }, this);
19945     },
19946     
19947     
19948     // private
19949     timeout : null,
19950     hoverState : null,
19951     
19952     toggle : function () {
19953         this.hoverState == 'in' ? this.leave() : this.enter();
19954     },
19955     
19956     enter : function () {
19957         
19958         clearTimeout(this.timeout);
19959     
19960         this.hoverState = 'in';
19961     
19962         if (!this.delay || !this.delay.show) {
19963             this.show();
19964             return;
19965         }
19966         var _t = this;
19967         this.timeout = setTimeout(function () {
19968             if (_t.hoverState == 'in') {
19969                 _t.show();
19970             }
19971         }, this.delay.show)
19972     },
19973     
19974     leave : function() {
19975         clearTimeout(this.timeout);
19976     
19977         this.hoverState = 'out';
19978     
19979         if (!this.delay || !this.delay.hide) {
19980             this.hide();
19981             return;
19982         }
19983         var _t = this;
19984         this.timeout = setTimeout(function () {
19985             if (_t.hoverState == 'out') {
19986                 _t.hide();
19987             }
19988         }, this.delay.hide)
19989     },
19990     /**
19991      * Show the popover
19992      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19993      * @param {string} (left|right|top|bottom) position
19994      */
19995     show : function (on_el, placement)
19996     {
19997         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19998         on_el = on_el || false; // default to false
19999          
20000         if (!on_el) {
20001             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20002                 on_el = this.parent().el;
20003             } else if (this.over) {
20004                 Roo.get(this.over);
20005             }
20006             
20007         }
20008         
20009         this.alignEl = Roo.get( on_el );
20010
20011         if (!this.el) {
20012             this.render(document.body);
20013         }
20014         
20015         
20016          
20017         
20018         if (this.title === false) {
20019             this.headerEl.hide();
20020         }
20021         
20022        
20023         this.el.show();
20024         this.el.dom.style.display = 'block';
20025          
20026  
20027         if (this.alignEl) {
20028             this.updatePosition(this.placement, true);
20029              
20030         } else {
20031             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20032             var es = this.el.getSize();
20033             var x = Roo.lib.Dom.getViewWidth()/2;
20034             var y = Roo.lib.Dom.getViewHeight()/2;
20035             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20036             
20037         }
20038
20039         
20040         //var arrow = this.el.select('.arrow',true).first();
20041         //arrow.set(align[2], 
20042         
20043         this.el.addClass('in');
20044         
20045          
20046         
20047         this.hoverState = 'in';
20048         
20049         if (this.modal) {
20050             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20051             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20052             this.maskEl.dom.style.display = 'block';
20053             this.maskEl.addClass('show');
20054         }
20055         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20056  
20057         this.fireEvent('show', this);
20058         
20059     },
20060     /**
20061      * fire this manually after loading a grid in the table for example
20062      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20063      * @param {Boolean} try and move it if we cant get right position.
20064      */
20065     updatePosition : function(placement, try_move)
20066     {
20067         // allow for calling with no parameters
20068         placement = placement   ? placement :  this.placement;
20069         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20070         
20071         this.el.removeClass([
20072             'fade','top','bottom', 'left', 'right','in',
20073             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20074         ]);
20075         this.el.addClass(placement + ' bs-popover-' + placement);
20076         
20077         if (!this.alignEl ) {
20078             return false;
20079         }
20080         
20081         switch (placement) {
20082             case 'right':
20083                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20084                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20085                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20086                     //normal display... or moved up/down.
20087                     this.el.setXY(offset);
20088                     var xy = this.alignEl.getAnchorXY('tr', false);
20089                     xy[0]+=2;xy[1]+=5;
20090                     this.arrowEl.setXY(xy);
20091                     return true;
20092                 }
20093                 // continue through...
20094                 return this.updatePosition('left', false);
20095                 
20096             
20097             case 'left':
20098                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20099                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20100                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20101                     //normal display... or moved up/down.
20102                     this.el.setXY(offset);
20103                     var xy = this.alignEl.getAnchorXY('tl', false);
20104                     xy[0]-=10;xy[1]+=5; // << fix me
20105                     this.arrowEl.setXY(xy);
20106                     return true;
20107                 }
20108                 // call self...
20109                 return this.updatePosition('right', false);
20110             
20111             case 'top':
20112                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20113                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20114                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20115                     //normal display... or moved up/down.
20116                     this.el.setXY(offset);
20117                     var xy = this.alignEl.getAnchorXY('t', false);
20118                     xy[1]-=10; // << fix me
20119                     this.arrowEl.setXY(xy);
20120                     return true;
20121                 }
20122                 // fall through
20123                return this.updatePosition('bottom', false);
20124             
20125             case 'bottom':
20126                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20127                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20128                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20129                     //normal display... or moved up/down.
20130                     this.el.setXY(offset);
20131                     var xy = this.alignEl.getAnchorXY('b', false);
20132                      xy[1]+=2; // << fix me
20133                     this.arrowEl.setXY(xy);
20134                     return true;
20135                 }
20136                 // fall through
20137                 return this.updatePosition('top', false);
20138                 
20139             
20140         }
20141         
20142         
20143         return false;
20144     },
20145     
20146     hide : function()
20147     {
20148         this.el.setXY([0,0]);
20149         this.el.removeClass('in');
20150         this.el.hide();
20151         this.hoverState = null;
20152         this.maskEl.hide(); // always..
20153         this.fireEvent('hide', this);
20154     }
20155     
20156 });
20157
20158
20159 Roo.apply(Roo.bootstrap.Popover, {
20160
20161     alignment : {
20162         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20163         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20164         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20165         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20166     },
20167     
20168     zIndex : 20001,
20169
20170     clickHander : false,
20171     
20172
20173     onMouseDown : function(e)
20174     {
20175         if (!e.getTarget(".roo-popover")) {
20176             this.hideAll();
20177         }
20178          
20179     },
20180     
20181     popups : [],
20182     
20183     register : function(popup)
20184     {
20185         if (!Roo.bootstrap.Popover.clickHandler) {
20186             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20187         }
20188         // hide other popups.
20189         this.hideAll();
20190         this.popups.push(popup);
20191     },
20192     hideAll : function()
20193     {
20194         this.popups.forEach(function(p) {
20195             p.hide();
20196         });
20197     }
20198
20199 });/*
20200  * - LGPL
20201  *
20202  * Card header - holder for the card header elements.
20203  * 
20204  */
20205
20206 /**
20207  * @class Roo.bootstrap.PopoverNav
20208  * @extends Roo.bootstrap.NavGroup
20209  * Bootstrap Popover header navigation class
20210  * @constructor
20211  * Create a new Popover Header Navigation 
20212  * @param {Object} config The config object
20213  */
20214
20215 Roo.bootstrap.PopoverNav = function(config){
20216     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20217 };
20218
20219 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20220     
20221     
20222     container_method : 'getPopoverHeader' 
20223     
20224      
20225     
20226     
20227    
20228 });
20229
20230  
20231
20232  /*
20233  * - LGPL
20234  *
20235  * Progress
20236  * 
20237  */
20238
20239 /**
20240  * @class Roo.bootstrap.Progress
20241  * @extends Roo.bootstrap.Component
20242  * Bootstrap Progress class
20243  * @cfg {Boolean} striped striped of the progress bar
20244  * @cfg {Boolean} active animated of the progress bar
20245  * 
20246  * 
20247  * @constructor
20248  * Create a new Progress
20249  * @param {Object} config The config object
20250  */
20251
20252 Roo.bootstrap.Progress = function(config){
20253     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20254 };
20255
20256 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20257     
20258     striped : false,
20259     active: false,
20260     
20261     getAutoCreate : function(){
20262         var cfg = {
20263             tag: 'div',
20264             cls: 'progress'
20265         };
20266         
20267         
20268         if(this.striped){
20269             cfg.cls += ' progress-striped';
20270         }
20271       
20272         if(this.active){
20273             cfg.cls += ' active';
20274         }
20275         
20276         
20277         return cfg;
20278     }
20279    
20280 });
20281
20282  
20283
20284  /*
20285  * - LGPL
20286  *
20287  * ProgressBar
20288  * 
20289  */
20290
20291 /**
20292  * @class Roo.bootstrap.ProgressBar
20293  * @extends Roo.bootstrap.Component
20294  * Bootstrap ProgressBar class
20295  * @cfg {Number} aria_valuenow aria-value now
20296  * @cfg {Number} aria_valuemin aria-value min
20297  * @cfg {Number} aria_valuemax aria-value max
20298  * @cfg {String} label label for the progress bar
20299  * @cfg {String} panel (success | info | warning | danger )
20300  * @cfg {String} role role of the progress bar
20301  * @cfg {String} sr_only text
20302  * 
20303  * 
20304  * @constructor
20305  * Create a new ProgressBar
20306  * @param {Object} config The config object
20307  */
20308
20309 Roo.bootstrap.ProgressBar = function(config){
20310     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20311 };
20312
20313 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20314     
20315     aria_valuenow : 0,
20316     aria_valuemin : 0,
20317     aria_valuemax : 100,
20318     label : false,
20319     panel : false,
20320     role : false,
20321     sr_only: false,
20322     
20323     getAutoCreate : function()
20324     {
20325         
20326         var cfg = {
20327             tag: 'div',
20328             cls: 'progress-bar',
20329             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20330         };
20331         
20332         if(this.sr_only){
20333             cfg.cn = {
20334                 tag: 'span',
20335                 cls: 'sr-only',
20336                 html: this.sr_only
20337             }
20338         }
20339         
20340         if(this.role){
20341             cfg.role = this.role;
20342         }
20343         
20344         if(this.aria_valuenow){
20345             cfg['aria-valuenow'] = this.aria_valuenow;
20346         }
20347         
20348         if(this.aria_valuemin){
20349             cfg['aria-valuemin'] = this.aria_valuemin;
20350         }
20351         
20352         if(this.aria_valuemax){
20353             cfg['aria-valuemax'] = this.aria_valuemax;
20354         }
20355         
20356         if(this.label && !this.sr_only){
20357             cfg.html = this.label;
20358         }
20359         
20360         if(this.panel){
20361             cfg.cls += ' progress-bar-' + this.panel;
20362         }
20363         
20364         return cfg;
20365     },
20366     
20367     update : function(aria_valuenow)
20368     {
20369         this.aria_valuenow = aria_valuenow;
20370         
20371         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20372     }
20373    
20374 });
20375
20376  
20377
20378  /*
20379  * - LGPL
20380  *
20381  * column
20382  * 
20383  */
20384
20385 /**
20386  * @class Roo.bootstrap.TabGroup
20387  * @extends Roo.bootstrap.Column
20388  * Bootstrap Column class
20389  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20390  * @cfg {Boolean} carousel true to make the group behave like a carousel
20391  * @cfg {Boolean} bullets show bullets for the panels
20392  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20393  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20394  * @cfg {Boolean} showarrow (true|false) show arrow default true
20395  * 
20396  * @constructor
20397  * Create a new TabGroup
20398  * @param {Object} config The config object
20399  */
20400
20401 Roo.bootstrap.TabGroup = function(config){
20402     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20403     if (!this.navId) {
20404         this.navId = Roo.id();
20405     }
20406     this.tabs = [];
20407     Roo.bootstrap.TabGroup.register(this);
20408     
20409 };
20410
20411 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20412     
20413     carousel : false,
20414     transition : false,
20415     bullets : 0,
20416     timer : 0,
20417     autoslide : false,
20418     slideFn : false,
20419     slideOnTouch : false,
20420     showarrow : true,
20421     
20422     getAutoCreate : function()
20423     {
20424         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20425         
20426         cfg.cls += ' tab-content';
20427         
20428         if (this.carousel) {
20429             cfg.cls += ' carousel slide';
20430             
20431             cfg.cn = [{
20432                cls : 'carousel-inner',
20433                cn : []
20434             }];
20435         
20436             if(this.bullets  && !Roo.isTouch){
20437                 
20438                 var bullets = {
20439                     cls : 'carousel-bullets',
20440                     cn : []
20441                 };
20442                
20443                 if(this.bullets_cls){
20444                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20445                 }
20446                 
20447                 bullets.cn.push({
20448                     cls : 'clear'
20449                 });
20450                 
20451                 cfg.cn[0].cn.push(bullets);
20452             }
20453             
20454             if(this.showarrow){
20455                 cfg.cn[0].cn.push({
20456                     tag : 'div',
20457                     class : 'carousel-arrow',
20458                     cn : [
20459                         {
20460                             tag : 'div',
20461                             class : 'carousel-prev',
20462                             cn : [
20463                                 {
20464                                     tag : 'i',
20465                                     class : 'fa fa-chevron-left'
20466                                 }
20467                             ]
20468                         },
20469                         {
20470                             tag : 'div',
20471                             class : 'carousel-next',
20472                             cn : [
20473                                 {
20474                                     tag : 'i',
20475                                     class : 'fa fa-chevron-right'
20476                                 }
20477                             ]
20478                         }
20479                     ]
20480                 });
20481             }
20482             
20483         }
20484         
20485         return cfg;
20486     },
20487     
20488     initEvents:  function()
20489     {
20490 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20491 //            this.el.on("touchstart", this.onTouchStart, this);
20492 //        }
20493         
20494         if(this.autoslide){
20495             var _this = this;
20496             
20497             this.slideFn = window.setInterval(function() {
20498                 _this.showPanelNext();
20499             }, this.timer);
20500         }
20501         
20502         if(this.showarrow){
20503             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20504             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20505         }
20506         
20507         
20508     },
20509     
20510 //    onTouchStart : function(e, el, o)
20511 //    {
20512 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20513 //            return;
20514 //        }
20515 //        
20516 //        this.showPanelNext();
20517 //    },
20518     
20519     
20520     getChildContainer : function()
20521     {
20522         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20523     },
20524     
20525     /**
20526     * register a Navigation item
20527     * @param {Roo.bootstrap.NavItem} the navitem to add
20528     */
20529     register : function(item)
20530     {
20531         this.tabs.push( item);
20532         item.navId = this.navId; // not really needed..
20533         this.addBullet();
20534     
20535     },
20536     
20537     getActivePanel : function()
20538     {
20539         var r = false;
20540         Roo.each(this.tabs, function(t) {
20541             if (t.active) {
20542                 r = t;
20543                 return false;
20544             }
20545             return null;
20546         });
20547         return r;
20548         
20549     },
20550     getPanelByName : function(n)
20551     {
20552         var r = false;
20553         Roo.each(this.tabs, function(t) {
20554             if (t.tabId == n) {
20555                 r = t;
20556                 return false;
20557             }
20558             return null;
20559         });
20560         return r;
20561     },
20562     indexOfPanel : function(p)
20563     {
20564         var r = false;
20565         Roo.each(this.tabs, function(t,i) {
20566             if (t.tabId == p.tabId) {
20567                 r = i;
20568                 return false;
20569             }
20570             return null;
20571         });
20572         return r;
20573     },
20574     /**
20575      * show a specific panel
20576      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20577      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20578      */
20579     showPanel : function (pan)
20580     {
20581         if(this.transition || typeof(pan) == 'undefined'){
20582             Roo.log("waiting for the transitionend");
20583             return false;
20584         }
20585         
20586         if (typeof(pan) == 'number') {
20587             pan = this.tabs[pan];
20588         }
20589         
20590         if (typeof(pan) == 'string') {
20591             pan = this.getPanelByName(pan);
20592         }
20593         
20594         var cur = this.getActivePanel();
20595         
20596         if(!pan || !cur){
20597             Roo.log('pan or acitve pan is undefined');
20598             return false;
20599         }
20600         
20601         if (pan.tabId == this.getActivePanel().tabId) {
20602             return true;
20603         }
20604         
20605         if (false === cur.fireEvent('beforedeactivate')) {
20606             return false;
20607         }
20608         
20609         if(this.bullets > 0 && !Roo.isTouch){
20610             this.setActiveBullet(this.indexOfPanel(pan));
20611         }
20612         
20613         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20614             
20615             //class="carousel-item carousel-item-next carousel-item-left"
20616             
20617             this.transition = true;
20618             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20619             var lr = dir == 'next' ? 'left' : 'right';
20620             pan.el.addClass(dir); // or prev
20621             pan.el.addClass('carousel-item-' + dir); // or prev
20622             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20623             cur.el.addClass(lr); // or right
20624             pan.el.addClass(lr);
20625             cur.el.addClass('carousel-item-' +lr); // or right
20626             pan.el.addClass('carousel-item-' +lr);
20627             
20628             
20629             var _this = this;
20630             cur.el.on('transitionend', function() {
20631                 Roo.log("trans end?");
20632                 
20633                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20634                 pan.setActive(true);
20635                 
20636                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20637                 cur.setActive(false);
20638                 
20639                 _this.transition = false;
20640                 
20641             }, this, { single:  true } );
20642             
20643             return true;
20644         }
20645         
20646         cur.setActive(false);
20647         pan.setActive(true);
20648         
20649         return true;
20650         
20651     },
20652     showPanelNext : function()
20653     {
20654         var i = this.indexOfPanel(this.getActivePanel());
20655         
20656         if (i >= this.tabs.length - 1 && !this.autoslide) {
20657             return;
20658         }
20659         
20660         if (i >= this.tabs.length - 1 && this.autoslide) {
20661             i = -1;
20662         }
20663         
20664         this.showPanel(this.tabs[i+1]);
20665     },
20666     
20667     showPanelPrev : function()
20668     {
20669         var i = this.indexOfPanel(this.getActivePanel());
20670         
20671         if (i  < 1 && !this.autoslide) {
20672             return;
20673         }
20674         
20675         if (i < 1 && this.autoslide) {
20676             i = this.tabs.length;
20677         }
20678         
20679         this.showPanel(this.tabs[i-1]);
20680     },
20681     
20682     
20683     addBullet: function()
20684     {
20685         if(!this.bullets || Roo.isTouch){
20686             return;
20687         }
20688         var ctr = this.el.select('.carousel-bullets',true).first();
20689         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20690         var bullet = ctr.createChild({
20691             cls : 'bullet bullet-' + i
20692         },ctr.dom.lastChild);
20693         
20694         
20695         var _this = this;
20696         
20697         bullet.on('click', (function(e, el, o, ii, t){
20698
20699             e.preventDefault();
20700
20701             this.showPanel(ii);
20702
20703             if(this.autoslide && this.slideFn){
20704                 clearInterval(this.slideFn);
20705                 this.slideFn = window.setInterval(function() {
20706                     _this.showPanelNext();
20707                 }, this.timer);
20708             }
20709
20710         }).createDelegate(this, [i, bullet], true));
20711                 
20712         
20713     },
20714      
20715     setActiveBullet : function(i)
20716     {
20717         if(Roo.isTouch){
20718             return;
20719         }
20720         
20721         Roo.each(this.el.select('.bullet', true).elements, function(el){
20722             el.removeClass('selected');
20723         });
20724
20725         var bullet = this.el.select('.bullet-' + i, true).first();
20726         
20727         if(!bullet){
20728             return;
20729         }
20730         
20731         bullet.addClass('selected');
20732     }
20733     
20734     
20735   
20736 });
20737
20738  
20739
20740  
20741  
20742 Roo.apply(Roo.bootstrap.TabGroup, {
20743     
20744     groups: {},
20745      /**
20746     * register a Navigation Group
20747     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20748     */
20749     register : function(navgrp)
20750     {
20751         this.groups[navgrp.navId] = navgrp;
20752         
20753     },
20754     /**
20755     * fetch a Navigation Group based on the navigation ID
20756     * if one does not exist , it will get created.
20757     * @param {string} the navgroup to add
20758     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20759     */
20760     get: function(navId) {
20761         if (typeof(this.groups[navId]) == 'undefined') {
20762             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20763         }
20764         return this.groups[navId] ;
20765     }
20766     
20767     
20768     
20769 });
20770
20771  /*
20772  * - LGPL
20773  *
20774  * TabPanel
20775  * 
20776  */
20777
20778 /**
20779  * @class Roo.bootstrap.TabPanel
20780  * @extends Roo.bootstrap.Component
20781  * Bootstrap TabPanel class
20782  * @cfg {Boolean} active panel active
20783  * @cfg {String} html panel content
20784  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20785  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20786  * @cfg {String} href click to link..
20787  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20788  * 
20789  * 
20790  * @constructor
20791  * Create a new TabPanel
20792  * @param {Object} config The config object
20793  */
20794
20795 Roo.bootstrap.TabPanel = function(config){
20796     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20797     this.addEvents({
20798         /**
20799              * @event changed
20800              * Fires when the active status changes
20801              * @param {Roo.bootstrap.TabPanel} this
20802              * @param {Boolean} state the new state
20803             
20804          */
20805         'changed': true,
20806         /**
20807              * @event beforedeactivate
20808              * Fires before a tab is de-activated - can be used to do validation on a form.
20809              * @param {Roo.bootstrap.TabPanel} this
20810              * @return {Boolean} false if there is an error
20811             
20812          */
20813         'beforedeactivate': true
20814      });
20815     
20816     this.tabId = this.tabId || Roo.id();
20817   
20818 };
20819
20820 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20821     
20822     active: false,
20823     html: false,
20824     tabId: false,
20825     navId : false,
20826     href : '',
20827     touchSlide : false,
20828     getAutoCreate : function(){
20829         
20830         
20831         var cfg = {
20832             tag: 'div',
20833             // item is needed for carousel - not sure if it has any effect otherwise
20834             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20835             html: this.html || ''
20836         };
20837         
20838         if(this.active){
20839             cfg.cls += ' active';
20840         }
20841         
20842         if(this.tabId){
20843             cfg.tabId = this.tabId;
20844         }
20845         
20846         
20847         
20848         return cfg;
20849     },
20850     
20851     initEvents:  function()
20852     {
20853         var p = this.parent();
20854         
20855         this.navId = this.navId || p.navId;
20856         
20857         if (typeof(this.navId) != 'undefined') {
20858             // not really needed.. but just in case.. parent should be a NavGroup.
20859             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20860             
20861             tg.register(this);
20862             
20863             var i = tg.tabs.length - 1;
20864             
20865             if(this.active && tg.bullets > 0 && i < tg.bullets){
20866                 tg.setActiveBullet(i);
20867             }
20868         }
20869         
20870         this.el.on('click', this.onClick, this);
20871         
20872         if(Roo.isTouch && this.touchSlide){
20873             this.el.on("touchstart", this.onTouchStart, this);
20874             this.el.on("touchmove", this.onTouchMove, this);
20875             this.el.on("touchend", this.onTouchEnd, this);
20876         }
20877         
20878     },
20879     
20880     onRender : function(ct, position)
20881     {
20882         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20883     },
20884     
20885     setActive : function(state)
20886     {
20887         Roo.log("panel - set active " + this.tabId + "=" + state);
20888         
20889         this.active = state;
20890         if (!state) {
20891             this.el.removeClass('active');
20892             
20893         } else  if (!this.el.hasClass('active')) {
20894             this.el.addClass('active');
20895         }
20896         
20897         this.fireEvent('changed', this, state);
20898     },
20899     
20900     onClick : function(e)
20901     {
20902         e.preventDefault();
20903         
20904         if(!this.href.length){
20905             return;
20906         }
20907         
20908         window.location.href = this.href;
20909     },
20910     
20911     startX : 0,
20912     startY : 0,
20913     endX : 0,
20914     endY : 0,
20915     swiping : false,
20916     
20917     onTouchStart : function(e)
20918     {
20919         this.swiping = false;
20920         
20921         this.startX = e.browserEvent.touches[0].clientX;
20922         this.startY = e.browserEvent.touches[0].clientY;
20923     },
20924     
20925     onTouchMove : function(e)
20926     {
20927         this.swiping = true;
20928         
20929         this.endX = e.browserEvent.touches[0].clientX;
20930         this.endY = e.browserEvent.touches[0].clientY;
20931     },
20932     
20933     onTouchEnd : function(e)
20934     {
20935         if(!this.swiping){
20936             this.onClick(e);
20937             return;
20938         }
20939         
20940         var tabGroup = this.parent();
20941         
20942         if(this.endX > this.startX){ // swiping right
20943             tabGroup.showPanelPrev();
20944             return;
20945         }
20946         
20947         if(this.startX > this.endX){ // swiping left
20948             tabGroup.showPanelNext();
20949             return;
20950         }
20951     }
20952     
20953     
20954 });
20955  
20956
20957  
20958
20959  /*
20960  * - LGPL
20961  *
20962  * DateField
20963  * 
20964  */
20965
20966 /**
20967  * @class Roo.bootstrap.DateField
20968  * @extends Roo.bootstrap.Input
20969  * Bootstrap DateField class
20970  * @cfg {Number} weekStart default 0
20971  * @cfg {String} viewMode default empty, (months|years)
20972  * @cfg {String} minViewMode default empty, (months|years)
20973  * @cfg {Number} startDate default -Infinity
20974  * @cfg {Number} endDate default Infinity
20975  * @cfg {Boolean} todayHighlight default false
20976  * @cfg {Boolean} todayBtn default false
20977  * @cfg {Boolean} calendarWeeks default false
20978  * @cfg {Object} daysOfWeekDisabled default empty
20979  * @cfg {Boolean} singleMode default false (true | false)
20980  * 
20981  * @cfg {Boolean} keyboardNavigation default true
20982  * @cfg {String} language default en
20983  * 
20984  * @constructor
20985  * Create a new DateField
20986  * @param {Object} config The config object
20987  */
20988
20989 Roo.bootstrap.DateField = function(config){
20990     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20991      this.addEvents({
20992             /**
20993              * @event show
20994              * Fires when this field show.
20995              * @param {Roo.bootstrap.DateField} this
20996              * @param {Mixed} date The date value
20997              */
20998             show : true,
20999             /**
21000              * @event show
21001              * Fires when this field hide.
21002              * @param {Roo.bootstrap.DateField} this
21003              * @param {Mixed} date The date value
21004              */
21005             hide : true,
21006             /**
21007              * @event select
21008              * Fires when select a date.
21009              * @param {Roo.bootstrap.DateField} this
21010              * @param {Mixed} date The date value
21011              */
21012             select : true,
21013             /**
21014              * @event beforeselect
21015              * Fires when before select a date.
21016              * @param {Roo.bootstrap.DateField} this
21017              * @param {Mixed} date The date value
21018              */
21019             beforeselect : true
21020         });
21021 };
21022
21023 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21024     
21025     /**
21026      * @cfg {String} format
21027      * The default date format string which can be overriden for localization support.  The format must be
21028      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21029      */
21030     format : "m/d/y",
21031     /**
21032      * @cfg {String} altFormats
21033      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21034      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21035      */
21036     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21037     
21038     weekStart : 0,
21039     
21040     viewMode : '',
21041     
21042     minViewMode : '',
21043     
21044     todayHighlight : false,
21045     
21046     todayBtn: false,
21047     
21048     language: 'en',
21049     
21050     keyboardNavigation: true,
21051     
21052     calendarWeeks: false,
21053     
21054     startDate: -Infinity,
21055     
21056     endDate: Infinity,
21057     
21058     daysOfWeekDisabled: [],
21059     
21060     _events: [],
21061     
21062     singleMode : false,
21063     
21064     UTCDate: function()
21065     {
21066         return new Date(Date.UTC.apply(Date, arguments));
21067     },
21068     
21069     UTCToday: function()
21070     {
21071         var today = new Date();
21072         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21073     },
21074     
21075     getDate: function() {
21076             var d = this.getUTCDate();
21077             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21078     },
21079     
21080     getUTCDate: function() {
21081             return this.date;
21082     },
21083     
21084     setDate: function(d) {
21085             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21086     },
21087     
21088     setUTCDate: function(d) {
21089             this.date = d;
21090             this.setValue(this.formatDate(this.date));
21091     },
21092         
21093     onRender: function(ct, position)
21094     {
21095         
21096         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21097         
21098         this.language = this.language || 'en';
21099         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21100         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21101         
21102         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21103         this.format = this.format || 'm/d/y';
21104         this.isInline = false;
21105         this.isInput = true;
21106         this.component = this.el.select('.add-on', true).first() || false;
21107         this.component = (this.component && this.component.length === 0) ? false : this.component;
21108         this.hasInput = this.component && this.inputEl().length;
21109         
21110         if (typeof(this.minViewMode === 'string')) {
21111             switch (this.minViewMode) {
21112                 case 'months':
21113                     this.minViewMode = 1;
21114                     break;
21115                 case 'years':
21116                     this.minViewMode = 2;
21117                     break;
21118                 default:
21119                     this.minViewMode = 0;
21120                     break;
21121             }
21122         }
21123         
21124         if (typeof(this.viewMode === 'string')) {
21125             switch (this.viewMode) {
21126                 case 'months':
21127                     this.viewMode = 1;
21128                     break;
21129                 case 'years':
21130                     this.viewMode = 2;
21131                     break;
21132                 default:
21133                     this.viewMode = 0;
21134                     break;
21135             }
21136         }
21137                 
21138         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21139         
21140 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21141         
21142         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21143         
21144         this.picker().on('mousedown', this.onMousedown, this);
21145         this.picker().on('click', this.onClick, this);
21146         
21147         this.picker().addClass('datepicker-dropdown');
21148         
21149         this.startViewMode = this.viewMode;
21150         
21151         if(this.singleMode){
21152             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21153                 v.setVisibilityMode(Roo.Element.DISPLAY);
21154                 v.hide();
21155             });
21156             
21157             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21158                 v.setStyle('width', '189px');
21159             });
21160         }
21161         
21162         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21163             if(!this.calendarWeeks){
21164                 v.remove();
21165                 return;
21166             }
21167             
21168             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21169             v.attr('colspan', function(i, val){
21170                 return parseInt(val) + 1;
21171             });
21172         });
21173                         
21174         
21175         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21176         
21177         this.setStartDate(this.startDate);
21178         this.setEndDate(this.endDate);
21179         
21180         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21181         
21182         this.fillDow();
21183         this.fillMonths();
21184         this.update();
21185         this.showMode();
21186         
21187         if(this.isInline) {
21188             this.showPopup();
21189         }
21190     },
21191     
21192     picker : function()
21193     {
21194         return this.pickerEl;
21195 //        return this.el.select('.datepicker', true).first();
21196     },
21197     
21198     fillDow: function()
21199     {
21200         var dowCnt = this.weekStart;
21201         
21202         var dow = {
21203             tag: 'tr',
21204             cn: [
21205                 
21206             ]
21207         };
21208         
21209         if(this.calendarWeeks){
21210             dow.cn.push({
21211                 tag: 'th',
21212                 cls: 'cw',
21213                 html: '&nbsp;'
21214             })
21215         }
21216         
21217         while (dowCnt < this.weekStart + 7) {
21218             dow.cn.push({
21219                 tag: 'th',
21220                 cls: 'dow',
21221                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21222             });
21223         }
21224         
21225         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21226     },
21227     
21228     fillMonths: function()
21229     {    
21230         var i = 0;
21231         var months = this.picker().select('>.datepicker-months td', true).first();
21232         
21233         months.dom.innerHTML = '';
21234         
21235         while (i < 12) {
21236             var month = {
21237                 tag: 'span',
21238                 cls: 'month',
21239                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21240             };
21241             
21242             months.createChild(month);
21243         }
21244         
21245     },
21246     
21247     update: function()
21248     {
21249         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;
21250         
21251         if (this.date < this.startDate) {
21252             this.viewDate = new Date(this.startDate);
21253         } else if (this.date > this.endDate) {
21254             this.viewDate = new Date(this.endDate);
21255         } else {
21256             this.viewDate = new Date(this.date);
21257         }
21258         
21259         this.fill();
21260     },
21261     
21262     fill: function() 
21263     {
21264         var d = new Date(this.viewDate),
21265                 year = d.getUTCFullYear(),
21266                 month = d.getUTCMonth(),
21267                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21268                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21269                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21270                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21271                 currentDate = this.date && this.date.valueOf(),
21272                 today = this.UTCToday();
21273         
21274         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21275         
21276 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21277         
21278 //        this.picker.select('>tfoot th.today').
21279 //                                              .text(dates[this.language].today)
21280 //                                              .toggle(this.todayBtn !== false);
21281     
21282         this.updateNavArrows();
21283         this.fillMonths();
21284                                                 
21285         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21286         
21287         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21288          
21289         prevMonth.setUTCDate(day);
21290         
21291         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21292         
21293         var nextMonth = new Date(prevMonth);
21294         
21295         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21296         
21297         nextMonth = nextMonth.valueOf();
21298         
21299         var fillMonths = false;
21300         
21301         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21302         
21303         while(prevMonth.valueOf() <= nextMonth) {
21304             var clsName = '';
21305             
21306             if (prevMonth.getUTCDay() === this.weekStart) {
21307                 if(fillMonths){
21308                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21309                 }
21310                     
21311                 fillMonths = {
21312                     tag: 'tr',
21313                     cn: []
21314                 };
21315                 
21316                 if(this.calendarWeeks){
21317                     // ISO 8601: First week contains first thursday.
21318                     // ISO also states week starts on Monday, but we can be more abstract here.
21319                     var
21320                     // Start of current week: based on weekstart/current date
21321                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21322                     // Thursday of this week
21323                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21324                     // First Thursday of year, year from thursday
21325                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21326                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21327                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21328                     
21329                     fillMonths.cn.push({
21330                         tag: 'td',
21331                         cls: 'cw',
21332                         html: calWeek
21333                     });
21334                 }
21335             }
21336             
21337             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21338                 clsName += ' old';
21339             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21340                 clsName += ' new';
21341             }
21342             if (this.todayHighlight &&
21343                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21344                 prevMonth.getUTCMonth() == today.getMonth() &&
21345                 prevMonth.getUTCDate() == today.getDate()) {
21346                 clsName += ' today';
21347             }
21348             
21349             if (currentDate && prevMonth.valueOf() === currentDate) {
21350                 clsName += ' active';
21351             }
21352             
21353             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21354                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21355                     clsName += ' disabled';
21356             }
21357             
21358             fillMonths.cn.push({
21359                 tag: 'td',
21360                 cls: 'day ' + clsName,
21361                 html: prevMonth.getDate()
21362             });
21363             
21364             prevMonth.setDate(prevMonth.getDate()+1);
21365         }
21366           
21367         var currentYear = this.date && this.date.getUTCFullYear();
21368         var currentMonth = this.date && this.date.getUTCMonth();
21369         
21370         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21371         
21372         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21373             v.removeClass('active');
21374             
21375             if(currentYear === year && k === currentMonth){
21376                 v.addClass('active');
21377             }
21378             
21379             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21380                 v.addClass('disabled');
21381             }
21382             
21383         });
21384         
21385         
21386         year = parseInt(year/10, 10) * 10;
21387         
21388         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21389         
21390         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21391         
21392         year -= 1;
21393         for (var i = -1; i < 11; i++) {
21394             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21395                 tag: 'span',
21396                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21397                 html: year
21398             });
21399             
21400             year += 1;
21401         }
21402     },
21403     
21404     showMode: function(dir) 
21405     {
21406         if (dir) {
21407             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21408         }
21409         
21410         Roo.each(this.picker().select('>div',true).elements, function(v){
21411             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21412             v.hide();
21413         });
21414         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21415     },
21416     
21417     place: function()
21418     {
21419         if(this.isInline) {
21420             return;
21421         }
21422         
21423         this.picker().removeClass(['bottom', 'top']);
21424         
21425         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21426             /*
21427              * place to the top of element!
21428              *
21429              */
21430             
21431             this.picker().addClass('top');
21432             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21433             
21434             return;
21435         }
21436         
21437         this.picker().addClass('bottom');
21438         
21439         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21440     },
21441     
21442     parseDate : function(value)
21443     {
21444         if(!value || value instanceof Date){
21445             return value;
21446         }
21447         var v = Date.parseDate(value, this.format);
21448         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21449             v = Date.parseDate(value, 'Y-m-d');
21450         }
21451         if(!v && this.altFormats){
21452             if(!this.altFormatsArray){
21453                 this.altFormatsArray = this.altFormats.split("|");
21454             }
21455             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21456                 v = Date.parseDate(value, this.altFormatsArray[i]);
21457             }
21458         }
21459         return v;
21460     },
21461     
21462     formatDate : function(date, fmt)
21463     {   
21464         return (!date || !(date instanceof Date)) ?
21465         date : date.dateFormat(fmt || this.format);
21466     },
21467     
21468     onFocus : function()
21469     {
21470         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21471         this.showPopup();
21472     },
21473     
21474     onBlur : function()
21475     {
21476         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21477         
21478         var d = this.inputEl().getValue();
21479         
21480         this.setValue(d);
21481                 
21482         this.hidePopup();
21483     },
21484     
21485     showPopup : function()
21486     {
21487         this.picker().show();
21488         this.update();
21489         this.place();
21490         
21491         this.fireEvent('showpopup', this, this.date);
21492     },
21493     
21494     hidePopup : function()
21495     {
21496         if(this.isInline) {
21497             return;
21498         }
21499         this.picker().hide();
21500         this.viewMode = this.startViewMode;
21501         this.showMode();
21502         
21503         this.fireEvent('hidepopup', this, this.date);
21504         
21505     },
21506     
21507     onMousedown: function(e)
21508     {
21509         e.stopPropagation();
21510         e.preventDefault();
21511     },
21512     
21513     keyup: function(e)
21514     {
21515         Roo.bootstrap.DateField.superclass.keyup.call(this);
21516         this.update();
21517     },
21518
21519     setValue: function(v)
21520     {
21521         if(this.fireEvent('beforeselect', this, v) !== false){
21522             var d = new Date(this.parseDate(v) ).clearTime();
21523         
21524             if(isNaN(d.getTime())){
21525                 this.date = this.viewDate = '';
21526                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21527                 return;
21528             }
21529
21530             v = this.formatDate(d);
21531
21532             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21533
21534             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21535
21536             this.update();
21537
21538             this.fireEvent('select', this, this.date);
21539         }
21540     },
21541     
21542     getValue: function()
21543     {
21544         return this.formatDate(this.date);
21545     },
21546     
21547     fireKey: function(e)
21548     {
21549         if (!this.picker().isVisible()){
21550             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21551                 this.showPopup();
21552             }
21553             return;
21554         }
21555         
21556         var dateChanged = false,
21557         dir, day, month,
21558         newDate, newViewDate;
21559         
21560         switch(e.keyCode){
21561             case 27: // escape
21562                 this.hidePopup();
21563                 e.preventDefault();
21564                 break;
21565             case 37: // left
21566             case 39: // right
21567                 if (!this.keyboardNavigation) {
21568                     break;
21569                 }
21570                 dir = e.keyCode == 37 ? -1 : 1;
21571                 
21572                 if (e.ctrlKey){
21573                     newDate = this.moveYear(this.date, dir);
21574                     newViewDate = this.moveYear(this.viewDate, dir);
21575                 } else if (e.shiftKey){
21576                     newDate = this.moveMonth(this.date, dir);
21577                     newViewDate = this.moveMonth(this.viewDate, dir);
21578                 } else {
21579                     newDate = new Date(this.date);
21580                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21581                     newViewDate = new Date(this.viewDate);
21582                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21583                 }
21584                 if (this.dateWithinRange(newDate)){
21585                     this.date = newDate;
21586                     this.viewDate = newViewDate;
21587                     this.setValue(this.formatDate(this.date));
21588 //                    this.update();
21589                     e.preventDefault();
21590                     dateChanged = true;
21591                 }
21592                 break;
21593             case 38: // up
21594             case 40: // down
21595                 if (!this.keyboardNavigation) {
21596                     break;
21597                 }
21598                 dir = e.keyCode == 38 ? -1 : 1;
21599                 if (e.ctrlKey){
21600                     newDate = this.moveYear(this.date, dir);
21601                     newViewDate = this.moveYear(this.viewDate, dir);
21602                 } else if (e.shiftKey){
21603                     newDate = this.moveMonth(this.date, dir);
21604                     newViewDate = this.moveMonth(this.viewDate, dir);
21605                 } else {
21606                     newDate = new Date(this.date);
21607                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21608                     newViewDate = new Date(this.viewDate);
21609                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21610                 }
21611                 if (this.dateWithinRange(newDate)){
21612                     this.date = newDate;
21613                     this.viewDate = newViewDate;
21614                     this.setValue(this.formatDate(this.date));
21615 //                    this.update();
21616                     e.preventDefault();
21617                     dateChanged = true;
21618                 }
21619                 break;
21620             case 13: // enter
21621                 this.setValue(this.formatDate(this.date));
21622                 this.hidePopup();
21623                 e.preventDefault();
21624                 break;
21625             case 9: // tab
21626                 this.setValue(this.formatDate(this.date));
21627                 this.hidePopup();
21628                 break;
21629             case 16: // shift
21630             case 17: // ctrl
21631             case 18: // alt
21632                 break;
21633             default :
21634                 this.hidePopup();
21635                 
21636         }
21637     },
21638     
21639     
21640     onClick: function(e) 
21641     {
21642         e.stopPropagation();
21643         e.preventDefault();
21644         
21645         var target = e.getTarget();
21646         
21647         if(target.nodeName.toLowerCase() === 'i'){
21648             target = Roo.get(target).dom.parentNode;
21649         }
21650         
21651         var nodeName = target.nodeName;
21652         var className = target.className;
21653         var html = target.innerHTML;
21654         //Roo.log(nodeName);
21655         
21656         switch(nodeName.toLowerCase()) {
21657             case 'th':
21658                 switch(className) {
21659                     case 'switch':
21660                         this.showMode(1);
21661                         break;
21662                     case 'prev':
21663                     case 'next':
21664                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21665                         switch(this.viewMode){
21666                                 case 0:
21667                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21668                                         break;
21669                                 case 1:
21670                                 case 2:
21671                                         this.viewDate = this.moveYear(this.viewDate, dir);
21672                                         break;
21673                         }
21674                         this.fill();
21675                         break;
21676                     case 'today':
21677                         var date = new Date();
21678                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21679 //                        this.fill()
21680                         this.setValue(this.formatDate(this.date));
21681                         
21682                         this.hidePopup();
21683                         break;
21684                 }
21685                 break;
21686             case 'span':
21687                 if (className.indexOf('disabled') < 0) {
21688                     this.viewDate.setUTCDate(1);
21689                     if (className.indexOf('month') > -1) {
21690                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21691                     } else {
21692                         var year = parseInt(html, 10) || 0;
21693                         this.viewDate.setUTCFullYear(year);
21694                         
21695                     }
21696                     
21697                     if(this.singleMode){
21698                         this.setValue(this.formatDate(this.viewDate));
21699                         this.hidePopup();
21700                         return;
21701                     }
21702                     
21703                     this.showMode(-1);
21704                     this.fill();
21705                 }
21706                 break;
21707                 
21708             case 'td':
21709                 //Roo.log(className);
21710                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21711                     var day = parseInt(html, 10) || 1;
21712                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21713                         month = (this.viewDate || new Date()).getUTCMonth();
21714
21715                     if (className.indexOf('old') > -1) {
21716                         if(month === 0 ){
21717                             month = 11;
21718                             year -= 1;
21719                         }else{
21720                             month -= 1;
21721                         }
21722                     } else if (className.indexOf('new') > -1) {
21723                         if (month == 11) {
21724                             month = 0;
21725                             year += 1;
21726                         } else {
21727                             month += 1;
21728                         }
21729                     }
21730                     //Roo.log([year,month,day]);
21731                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21732                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21733 //                    this.fill();
21734                     //Roo.log(this.formatDate(this.date));
21735                     this.setValue(this.formatDate(this.date));
21736                     this.hidePopup();
21737                 }
21738                 break;
21739         }
21740     },
21741     
21742     setStartDate: function(startDate)
21743     {
21744         this.startDate = startDate || -Infinity;
21745         if (this.startDate !== -Infinity) {
21746             this.startDate = this.parseDate(this.startDate);
21747         }
21748         this.update();
21749         this.updateNavArrows();
21750     },
21751
21752     setEndDate: function(endDate)
21753     {
21754         this.endDate = endDate || Infinity;
21755         if (this.endDate !== Infinity) {
21756             this.endDate = this.parseDate(this.endDate);
21757         }
21758         this.update();
21759         this.updateNavArrows();
21760     },
21761     
21762     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21763     {
21764         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21765         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21766             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21767         }
21768         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21769             return parseInt(d, 10);
21770         });
21771         this.update();
21772         this.updateNavArrows();
21773     },
21774     
21775     updateNavArrows: function() 
21776     {
21777         if(this.singleMode){
21778             return;
21779         }
21780         
21781         var d = new Date(this.viewDate),
21782         year = d.getUTCFullYear(),
21783         month = d.getUTCMonth();
21784         
21785         Roo.each(this.picker().select('.prev', true).elements, function(v){
21786             v.show();
21787             switch (this.viewMode) {
21788                 case 0:
21789
21790                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21791                         v.hide();
21792                     }
21793                     break;
21794                 case 1:
21795                 case 2:
21796                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21797                         v.hide();
21798                     }
21799                     break;
21800             }
21801         });
21802         
21803         Roo.each(this.picker().select('.next', true).elements, function(v){
21804             v.show();
21805             switch (this.viewMode) {
21806                 case 0:
21807
21808                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21809                         v.hide();
21810                     }
21811                     break;
21812                 case 1:
21813                 case 2:
21814                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21815                         v.hide();
21816                     }
21817                     break;
21818             }
21819         })
21820     },
21821     
21822     moveMonth: function(date, dir)
21823     {
21824         if (!dir) {
21825             return date;
21826         }
21827         var new_date = new Date(date.valueOf()),
21828         day = new_date.getUTCDate(),
21829         month = new_date.getUTCMonth(),
21830         mag = Math.abs(dir),
21831         new_month, test;
21832         dir = dir > 0 ? 1 : -1;
21833         if (mag == 1){
21834             test = dir == -1
21835             // If going back one month, make sure month is not current month
21836             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21837             ? function(){
21838                 return new_date.getUTCMonth() == month;
21839             }
21840             // If going forward one month, make sure month is as expected
21841             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21842             : function(){
21843                 return new_date.getUTCMonth() != new_month;
21844             };
21845             new_month = month + dir;
21846             new_date.setUTCMonth(new_month);
21847             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21848             if (new_month < 0 || new_month > 11) {
21849                 new_month = (new_month + 12) % 12;
21850             }
21851         } else {
21852             // For magnitudes >1, move one month at a time...
21853             for (var i=0; i<mag; i++) {
21854                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21855                 new_date = this.moveMonth(new_date, dir);
21856             }
21857             // ...then reset the day, keeping it in the new month
21858             new_month = new_date.getUTCMonth();
21859             new_date.setUTCDate(day);
21860             test = function(){
21861                 return new_month != new_date.getUTCMonth();
21862             };
21863         }
21864         // Common date-resetting loop -- if date is beyond end of month, make it
21865         // end of month
21866         while (test()){
21867             new_date.setUTCDate(--day);
21868             new_date.setUTCMonth(new_month);
21869         }
21870         return new_date;
21871     },
21872
21873     moveYear: function(date, dir)
21874     {
21875         return this.moveMonth(date, dir*12);
21876     },
21877
21878     dateWithinRange: function(date)
21879     {
21880         return date >= this.startDate && date <= this.endDate;
21881     },
21882
21883     
21884     remove: function() 
21885     {
21886         this.picker().remove();
21887     },
21888     
21889     validateValue : function(value)
21890     {
21891         if(this.getVisibilityEl().hasClass('hidden')){
21892             return true;
21893         }
21894         
21895         if(value.length < 1)  {
21896             if(this.allowBlank){
21897                 return true;
21898             }
21899             return false;
21900         }
21901         
21902         if(value.length < this.minLength){
21903             return false;
21904         }
21905         if(value.length > this.maxLength){
21906             return false;
21907         }
21908         if(this.vtype){
21909             var vt = Roo.form.VTypes;
21910             if(!vt[this.vtype](value, this)){
21911                 return false;
21912             }
21913         }
21914         if(typeof this.validator == "function"){
21915             var msg = this.validator(value);
21916             if(msg !== true){
21917                 return false;
21918             }
21919         }
21920         
21921         if(this.regex && !this.regex.test(value)){
21922             return false;
21923         }
21924         
21925         if(typeof(this.parseDate(value)) == 'undefined'){
21926             return false;
21927         }
21928         
21929         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21930             return false;
21931         }      
21932         
21933         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21934             return false;
21935         } 
21936         
21937         
21938         return true;
21939     },
21940     
21941     reset : function()
21942     {
21943         this.date = this.viewDate = '';
21944         
21945         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21946     }
21947    
21948 });
21949
21950 Roo.apply(Roo.bootstrap.DateField,  {
21951     
21952     head : {
21953         tag: 'thead',
21954         cn: [
21955         {
21956             tag: 'tr',
21957             cn: [
21958             {
21959                 tag: 'th',
21960                 cls: 'prev',
21961                 html: '<i class="fa fa-arrow-left"/>'
21962             },
21963             {
21964                 tag: 'th',
21965                 cls: 'switch',
21966                 colspan: '5'
21967             },
21968             {
21969                 tag: 'th',
21970                 cls: 'next',
21971                 html: '<i class="fa fa-arrow-right"/>'
21972             }
21973
21974             ]
21975         }
21976         ]
21977     },
21978     
21979     content : {
21980         tag: 'tbody',
21981         cn: [
21982         {
21983             tag: 'tr',
21984             cn: [
21985             {
21986                 tag: 'td',
21987                 colspan: '7'
21988             }
21989             ]
21990         }
21991         ]
21992     },
21993     
21994     footer : {
21995         tag: 'tfoot',
21996         cn: [
21997         {
21998             tag: 'tr',
21999             cn: [
22000             {
22001                 tag: 'th',
22002                 colspan: '7',
22003                 cls: 'today'
22004             }
22005                     
22006             ]
22007         }
22008         ]
22009     },
22010     
22011     dates:{
22012         en: {
22013             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22014             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22015             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22016             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22017             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22018             today: "Today"
22019         }
22020     },
22021     
22022     modes: [
22023     {
22024         clsName: 'days',
22025         navFnc: 'Month',
22026         navStep: 1
22027     },
22028     {
22029         clsName: 'months',
22030         navFnc: 'FullYear',
22031         navStep: 1
22032     },
22033     {
22034         clsName: 'years',
22035         navFnc: 'FullYear',
22036         navStep: 10
22037     }]
22038 });
22039
22040 Roo.apply(Roo.bootstrap.DateField,  {
22041   
22042     template : {
22043         tag: 'div',
22044         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22045         cn: [
22046         {
22047             tag: 'div',
22048             cls: 'datepicker-days',
22049             cn: [
22050             {
22051                 tag: 'table',
22052                 cls: 'table-condensed',
22053                 cn:[
22054                 Roo.bootstrap.DateField.head,
22055                 {
22056                     tag: 'tbody'
22057                 },
22058                 Roo.bootstrap.DateField.footer
22059                 ]
22060             }
22061             ]
22062         },
22063         {
22064             tag: 'div',
22065             cls: 'datepicker-months',
22066             cn: [
22067             {
22068                 tag: 'table',
22069                 cls: 'table-condensed',
22070                 cn:[
22071                 Roo.bootstrap.DateField.head,
22072                 Roo.bootstrap.DateField.content,
22073                 Roo.bootstrap.DateField.footer
22074                 ]
22075             }
22076             ]
22077         },
22078         {
22079             tag: 'div',
22080             cls: 'datepicker-years',
22081             cn: [
22082             {
22083                 tag: 'table',
22084                 cls: 'table-condensed',
22085                 cn:[
22086                 Roo.bootstrap.DateField.head,
22087                 Roo.bootstrap.DateField.content,
22088                 Roo.bootstrap.DateField.footer
22089                 ]
22090             }
22091             ]
22092         }
22093         ]
22094     }
22095 });
22096
22097  
22098
22099  /*
22100  * - LGPL
22101  *
22102  * TimeField
22103  * 
22104  */
22105
22106 /**
22107  * @class Roo.bootstrap.TimeField
22108  * @extends Roo.bootstrap.Input
22109  * Bootstrap DateField class
22110  * 
22111  * 
22112  * @constructor
22113  * Create a new TimeField
22114  * @param {Object} config The config object
22115  */
22116
22117 Roo.bootstrap.TimeField = function(config){
22118     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22119     this.addEvents({
22120             /**
22121              * @event show
22122              * Fires when this field show.
22123              * @param {Roo.bootstrap.DateField} thisthis
22124              * @param {Mixed} date The date value
22125              */
22126             show : true,
22127             /**
22128              * @event show
22129              * Fires when this field hide.
22130              * @param {Roo.bootstrap.DateField} this
22131              * @param {Mixed} date The date value
22132              */
22133             hide : true,
22134             /**
22135              * @event select
22136              * Fires when select a date.
22137              * @param {Roo.bootstrap.DateField} this
22138              * @param {Mixed} date The date value
22139              */
22140             select : true
22141         });
22142 };
22143
22144 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22145     
22146     /**
22147      * @cfg {String} format
22148      * The default time format string which can be overriden for localization support.  The format must be
22149      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22150      */
22151     format : "H:i",
22152
22153     getAutoCreate : function()
22154     {
22155         this.after = '<i class="fa far fa-clock"></i>';
22156         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22157         
22158          
22159     },
22160     onRender: function(ct, position)
22161     {
22162         
22163         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22164                 
22165         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22166         
22167         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22168         
22169         this.pop = this.picker().select('>.datepicker-time',true).first();
22170         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22171         
22172         this.picker().on('mousedown', this.onMousedown, this);
22173         this.picker().on('click', this.onClick, this);
22174         
22175         this.picker().addClass('datepicker-dropdown');
22176     
22177         this.fillTime();
22178         this.update();
22179             
22180         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22181         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22182         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22183         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22184         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22185         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22186
22187     },
22188     
22189     fireKey: function(e){
22190         if (!this.picker().isVisible()){
22191             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22192                 this.show();
22193             }
22194             return;
22195         }
22196
22197         e.preventDefault();
22198         
22199         switch(e.keyCode){
22200             case 27: // escape
22201                 this.hide();
22202                 break;
22203             case 37: // left
22204             case 39: // right
22205                 this.onTogglePeriod();
22206                 break;
22207             case 38: // up
22208                 this.onIncrementMinutes();
22209                 break;
22210             case 40: // down
22211                 this.onDecrementMinutes();
22212                 break;
22213             case 13: // enter
22214             case 9: // tab
22215                 this.setTime();
22216                 break;
22217         }
22218     },
22219     
22220     onClick: function(e) {
22221         e.stopPropagation();
22222         e.preventDefault();
22223     },
22224     
22225     picker : function()
22226     {
22227         return this.pickerEl;
22228     },
22229     
22230     fillTime: function()
22231     {    
22232         var time = this.pop.select('tbody', true).first();
22233         
22234         time.dom.innerHTML = '';
22235         
22236         time.createChild({
22237             tag: 'tr',
22238             cn: [
22239                 {
22240                     tag: 'td',
22241                     cn: [
22242                         {
22243                             tag: 'a',
22244                             href: '#',
22245                             cls: 'btn',
22246                             cn: [
22247                                 {
22248                                     tag: 'i',
22249                                     cls: 'hours-up fa fas fa-chevron-up'
22250                                 }
22251                             ]
22252                         } 
22253                     ]
22254                 },
22255                 {
22256                     tag: 'td',
22257                     cls: 'separator'
22258                 },
22259                 {
22260                     tag: 'td',
22261                     cn: [
22262                         {
22263                             tag: 'a',
22264                             href: '#',
22265                             cls: 'btn',
22266                             cn: [
22267                                 {
22268                                     tag: 'i',
22269                                     cls: 'minutes-up fa fas fa-chevron-up'
22270                                 }
22271                             ]
22272                         }
22273                     ]
22274                 },
22275                 {
22276                     tag: 'td',
22277                     cls: 'separator'
22278                 }
22279             ]
22280         });
22281         
22282         time.createChild({
22283             tag: 'tr',
22284             cn: [
22285                 {
22286                     tag: 'td',
22287                     cn: [
22288                         {
22289                             tag: 'span',
22290                             cls: 'timepicker-hour',
22291                             html: '00'
22292                         }  
22293                     ]
22294                 },
22295                 {
22296                     tag: 'td',
22297                     cls: 'separator',
22298                     html: ':'
22299                 },
22300                 {
22301                     tag: 'td',
22302                     cn: [
22303                         {
22304                             tag: 'span',
22305                             cls: 'timepicker-minute',
22306                             html: '00'
22307                         }  
22308                     ]
22309                 },
22310                 {
22311                     tag: 'td',
22312                     cls: 'separator'
22313                 },
22314                 {
22315                     tag: 'td',
22316                     cn: [
22317                         {
22318                             tag: 'button',
22319                             type: 'button',
22320                             cls: 'btn btn-primary period',
22321                             html: 'AM'
22322                             
22323                         }
22324                     ]
22325                 }
22326             ]
22327         });
22328         
22329         time.createChild({
22330             tag: 'tr',
22331             cn: [
22332                 {
22333                     tag: 'td',
22334                     cn: [
22335                         {
22336                             tag: 'a',
22337                             href: '#',
22338                             cls: 'btn',
22339                             cn: [
22340                                 {
22341                                     tag: 'span',
22342                                     cls: 'hours-down fa fas fa-chevron-down'
22343                                 }
22344                             ]
22345                         }
22346                     ]
22347                 },
22348                 {
22349                     tag: 'td',
22350                     cls: 'separator'
22351                 },
22352                 {
22353                     tag: 'td',
22354                     cn: [
22355                         {
22356                             tag: 'a',
22357                             href: '#',
22358                             cls: 'btn',
22359                             cn: [
22360                                 {
22361                                     tag: 'span',
22362                                     cls: 'minutes-down fa fas fa-chevron-down'
22363                                 }
22364                             ]
22365                         }
22366                     ]
22367                 },
22368                 {
22369                     tag: 'td',
22370                     cls: 'separator'
22371                 }
22372             ]
22373         });
22374         
22375     },
22376     
22377     update: function()
22378     {
22379         
22380         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22381         
22382         this.fill();
22383     },
22384     
22385     fill: function() 
22386     {
22387         var hours = this.time.getHours();
22388         var minutes = this.time.getMinutes();
22389         var period = 'AM';
22390         
22391         if(hours > 11){
22392             period = 'PM';
22393         }
22394         
22395         if(hours == 0){
22396             hours = 12;
22397         }
22398         
22399         
22400         if(hours > 12){
22401             hours = hours - 12;
22402         }
22403         
22404         if(hours < 10){
22405             hours = '0' + hours;
22406         }
22407         
22408         if(minutes < 10){
22409             minutes = '0' + minutes;
22410         }
22411         
22412         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22413         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22414         this.pop.select('button', true).first().dom.innerHTML = period;
22415         
22416     },
22417     
22418     place: function()
22419     {   
22420         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22421         
22422         var cls = ['bottom'];
22423         
22424         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22425             cls.pop();
22426             cls.push('top');
22427         }
22428         
22429         cls.push('right');
22430         
22431         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22432             cls.pop();
22433             cls.push('left');
22434         }
22435         //this.picker().setXY(20000,20000);
22436         this.picker().addClass(cls.join('-'));
22437         
22438         var _this = this;
22439         
22440         Roo.each(cls, function(c){
22441             if(c == 'bottom'){
22442                 (function() {
22443                  //  
22444                 }).defer(200);
22445                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22446                 //_this.picker().setTop(_this.inputEl().getHeight());
22447                 return;
22448             }
22449             if(c == 'top'){
22450                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22451                 
22452                 //_this.picker().setTop(0 - _this.picker().getHeight());
22453                 return;
22454             }
22455             /*
22456             if(c == 'left'){
22457                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22458                 return;
22459             }
22460             if(c == 'right'){
22461                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22462                 return;
22463             }
22464             */
22465         });
22466         
22467     },
22468   
22469     onFocus : function()
22470     {
22471         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22472         this.show();
22473     },
22474     
22475     onBlur : function()
22476     {
22477         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22478         this.hide();
22479     },
22480     
22481     show : function()
22482     {
22483         this.picker().show();
22484         this.pop.show();
22485         this.update();
22486         this.place();
22487         
22488         this.fireEvent('show', this, this.date);
22489     },
22490     
22491     hide : function()
22492     {
22493         this.picker().hide();
22494         this.pop.hide();
22495         
22496         this.fireEvent('hide', this, this.date);
22497     },
22498     
22499     setTime : function()
22500     {
22501         this.hide();
22502         this.setValue(this.time.format(this.format));
22503         
22504         this.fireEvent('select', this, this.date);
22505         
22506         
22507     },
22508     
22509     onMousedown: function(e){
22510         e.stopPropagation();
22511         e.preventDefault();
22512     },
22513     
22514     onIncrementHours: function()
22515     {
22516         Roo.log('onIncrementHours');
22517         this.time = this.time.add(Date.HOUR, 1);
22518         this.update();
22519         
22520     },
22521     
22522     onDecrementHours: function()
22523     {
22524         Roo.log('onDecrementHours');
22525         this.time = this.time.add(Date.HOUR, -1);
22526         this.update();
22527     },
22528     
22529     onIncrementMinutes: function()
22530     {
22531         Roo.log('onIncrementMinutes');
22532         this.time = this.time.add(Date.MINUTE, 1);
22533         this.update();
22534     },
22535     
22536     onDecrementMinutes: function()
22537     {
22538         Roo.log('onDecrementMinutes');
22539         this.time = this.time.add(Date.MINUTE, -1);
22540         this.update();
22541     },
22542     
22543     onTogglePeriod: function()
22544     {
22545         Roo.log('onTogglePeriod');
22546         this.time = this.time.add(Date.HOUR, 12);
22547         this.update();
22548     }
22549     
22550    
22551 });
22552  
22553
22554 Roo.apply(Roo.bootstrap.TimeField,  {
22555   
22556     template : {
22557         tag: 'div',
22558         cls: 'datepicker dropdown-menu',
22559         cn: [
22560             {
22561                 tag: 'div',
22562                 cls: 'datepicker-time',
22563                 cn: [
22564                 {
22565                     tag: 'table',
22566                     cls: 'table-condensed',
22567                     cn:[
22568                         {
22569                             tag: 'tbody',
22570                             cn: [
22571                                 {
22572                                     tag: 'tr',
22573                                     cn: [
22574                                     {
22575                                         tag: 'td',
22576                                         colspan: '7'
22577                                     }
22578                                     ]
22579                                 }
22580                             ]
22581                         },
22582                         {
22583                             tag: 'tfoot',
22584                             cn: [
22585                                 {
22586                                     tag: 'tr',
22587                                     cn: [
22588                                     {
22589                                         tag: 'th',
22590                                         colspan: '7',
22591                                         cls: '',
22592                                         cn: [
22593                                             {
22594                                                 tag: 'button',
22595                                                 cls: 'btn btn-info ok',
22596                                                 html: 'OK'
22597                                             }
22598                                         ]
22599                                     }
22600                     
22601                                     ]
22602                                 }
22603                             ]
22604                         }
22605                     ]
22606                 }
22607                 ]
22608             }
22609         ]
22610     }
22611 });
22612
22613  
22614
22615  /*
22616  * - LGPL
22617  *
22618  * MonthField
22619  * 
22620  */
22621
22622 /**
22623  * @class Roo.bootstrap.MonthField
22624  * @extends Roo.bootstrap.Input
22625  * Bootstrap MonthField class
22626  * 
22627  * @cfg {String} language default en
22628  * 
22629  * @constructor
22630  * Create a new MonthField
22631  * @param {Object} config The config object
22632  */
22633
22634 Roo.bootstrap.MonthField = function(config){
22635     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22636     
22637     this.addEvents({
22638         /**
22639          * @event show
22640          * Fires when this field show.
22641          * @param {Roo.bootstrap.MonthField} this
22642          * @param {Mixed} date The date value
22643          */
22644         show : true,
22645         /**
22646          * @event show
22647          * Fires when this field hide.
22648          * @param {Roo.bootstrap.MonthField} this
22649          * @param {Mixed} date The date value
22650          */
22651         hide : true,
22652         /**
22653          * @event select
22654          * Fires when select a date.
22655          * @param {Roo.bootstrap.MonthField} this
22656          * @param {String} oldvalue The old value
22657          * @param {String} newvalue The new value
22658          */
22659         select : true
22660     });
22661 };
22662
22663 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22664     
22665     onRender: function(ct, position)
22666     {
22667         
22668         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22669         
22670         this.language = this.language || 'en';
22671         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22672         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22673         
22674         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22675         this.isInline = false;
22676         this.isInput = true;
22677         this.component = this.el.select('.add-on', true).first() || false;
22678         this.component = (this.component && this.component.length === 0) ? false : this.component;
22679         this.hasInput = this.component && this.inputEL().length;
22680         
22681         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22682         
22683         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22684         
22685         this.picker().on('mousedown', this.onMousedown, this);
22686         this.picker().on('click', this.onClick, this);
22687         
22688         this.picker().addClass('datepicker-dropdown');
22689         
22690         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22691             v.setStyle('width', '189px');
22692         });
22693         
22694         this.fillMonths();
22695         
22696         this.update();
22697         
22698         if(this.isInline) {
22699             this.show();
22700         }
22701         
22702     },
22703     
22704     setValue: function(v, suppressEvent)
22705     {   
22706         var o = this.getValue();
22707         
22708         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22709         
22710         this.update();
22711
22712         if(suppressEvent !== true){
22713             this.fireEvent('select', this, o, v);
22714         }
22715         
22716     },
22717     
22718     getValue: function()
22719     {
22720         return this.value;
22721     },
22722     
22723     onClick: function(e) 
22724     {
22725         e.stopPropagation();
22726         e.preventDefault();
22727         
22728         var target = e.getTarget();
22729         
22730         if(target.nodeName.toLowerCase() === 'i'){
22731             target = Roo.get(target).dom.parentNode;
22732         }
22733         
22734         var nodeName = target.nodeName;
22735         var className = target.className;
22736         var html = target.innerHTML;
22737         
22738         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22739             return;
22740         }
22741         
22742         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22743         
22744         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22745         
22746         this.hide();
22747                         
22748     },
22749     
22750     picker : function()
22751     {
22752         return this.pickerEl;
22753     },
22754     
22755     fillMonths: function()
22756     {    
22757         var i = 0;
22758         var months = this.picker().select('>.datepicker-months td', true).first();
22759         
22760         months.dom.innerHTML = '';
22761         
22762         while (i < 12) {
22763             var month = {
22764                 tag: 'span',
22765                 cls: 'month',
22766                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22767             };
22768             
22769             months.createChild(month);
22770         }
22771         
22772     },
22773     
22774     update: function()
22775     {
22776         var _this = this;
22777         
22778         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22779             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22780         }
22781         
22782         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22783             e.removeClass('active');
22784             
22785             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22786                 e.addClass('active');
22787             }
22788         })
22789     },
22790     
22791     place: function()
22792     {
22793         if(this.isInline) {
22794             return;
22795         }
22796         
22797         this.picker().removeClass(['bottom', 'top']);
22798         
22799         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22800             /*
22801              * place to the top of element!
22802              *
22803              */
22804             
22805             this.picker().addClass('top');
22806             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22807             
22808             return;
22809         }
22810         
22811         this.picker().addClass('bottom');
22812         
22813         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22814     },
22815     
22816     onFocus : function()
22817     {
22818         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22819         this.show();
22820     },
22821     
22822     onBlur : function()
22823     {
22824         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22825         
22826         var d = this.inputEl().getValue();
22827         
22828         this.setValue(d);
22829                 
22830         this.hide();
22831     },
22832     
22833     show : function()
22834     {
22835         this.picker().show();
22836         this.picker().select('>.datepicker-months', true).first().show();
22837         this.update();
22838         this.place();
22839         
22840         this.fireEvent('show', this, this.date);
22841     },
22842     
22843     hide : function()
22844     {
22845         if(this.isInline) {
22846             return;
22847         }
22848         this.picker().hide();
22849         this.fireEvent('hide', this, this.date);
22850         
22851     },
22852     
22853     onMousedown: function(e)
22854     {
22855         e.stopPropagation();
22856         e.preventDefault();
22857     },
22858     
22859     keyup: function(e)
22860     {
22861         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22862         this.update();
22863     },
22864
22865     fireKey: function(e)
22866     {
22867         if (!this.picker().isVisible()){
22868             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22869                 this.show();
22870             }
22871             return;
22872         }
22873         
22874         var dir;
22875         
22876         switch(e.keyCode){
22877             case 27: // escape
22878                 this.hide();
22879                 e.preventDefault();
22880                 break;
22881             case 37: // left
22882             case 39: // right
22883                 dir = e.keyCode == 37 ? -1 : 1;
22884                 
22885                 this.vIndex = this.vIndex + dir;
22886                 
22887                 if(this.vIndex < 0){
22888                     this.vIndex = 0;
22889                 }
22890                 
22891                 if(this.vIndex > 11){
22892                     this.vIndex = 11;
22893                 }
22894                 
22895                 if(isNaN(this.vIndex)){
22896                     this.vIndex = 0;
22897                 }
22898                 
22899                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22900                 
22901                 break;
22902             case 38: // up
22903             case 40: // down
22904                 
22905                 dir = e.keyCode == 38 ? -1 : 1;
22906                 
22907                 this.vIndex = this.vIndex + dir * 4;
22908                 
22909                 if(this.vIndex < 0){
22910                     this.vIndex = 0;
22911                 }
22912                 
22913                 if(this.vIndex > 11){
22914                     this.vIndex = 11;
22915                 }
22916                 
22917                 if(isNaN(this.vIndex)){
22918                     this.vIndex = 0;
22919                 }
22920                 
22921                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22922                 break;
22923                 
22924             case 13: // enter
22925                 
22926                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22927                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22928                 }
22929                 
22930                 this.hide();
22931                 e.preventDefault();
22932                 break;
22933             case 9: // tab
22934                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22935                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22936                 }
22937                 this.hide();
22938                 break;
22939             case 16: // shift
22940             case 17: // ctrl
22941             case 18: // alt
22942                 break;
22943             default :
22944                 this.hide();
22945                 
22946         }
22947     },
22948     
22949     remove: function() 
22950     {
22951         this.picker().remove();
22952     }
22953    
22954 });
22955
22956 Roo.apply(Roo.bootstrap.MonthField,  {
22957     
22958     content : {
22959         tag: 'tbody',
22960         cn: [
22961         {
22962             tag: 'tr',
22963             cn: [
22964             {
22965                 tag: 'td',
22966                 colspan: '7'
22967             }
22968             ]
22969         }
22970         ]
22971     },
22972     
22973     dates:{
22974         en: {
22975             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22976             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22977         }
22978     }
22979 });
22980
22981 Roo.apply(Roo.bootstrap.MonthField,  {
22982   
22983     template : {
22984         tag: 'div',
22985         cls: 'datepicker dropdown-menu roo-dynamic',
22986         cn: [
22987             {
22988                 tag: 'div',
22989                 cls: 'datepicker-months',
22990                 cn: [
22991                 {
22992                     tag: 'table',
22993                     cls: 'table-condensed',
22994                     cn:[
22995                         Roo.bootstrap.DateField.content
22996                     ]
22997                 }
22998                 ]
22999             }
23000         ]
23001     }
23002 });
23003
23004  
23005
23006  
23007  /*
23008  * - LGPL
23009  *
23010  * CheckBox
23011  * 
23012  */
23013
23014 /**
23015  * @class Roo.bootstrap.CheckBox
23016  * @extends Roo.bootstrap.Input
23017  * Bootstrap CheckBox class
23018  * 
23019  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23020  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23021  * @cfg {String} boxLabel The text that appears beside the checkbox
23022  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23023  * @cfg {Boolean} checked initnal the element
23024  * @cfg {Boolean} inline inline the element (default false)
23025  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23026  * @cfg {String} tooltip label tooltip
23027  * 
23028  * @constructor
23029  * Create a new CheckBox
23030  * @param {Object} config The config object
23031  */
23032
23033 Roo.bootstrap.CheckBox = function(config){
23034     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23035    
23036     this.addEvents({
23037         /**
23038         * @event check
23039         * Fires when the element is checked or unchecked.
23040         * @param {Roo.bootstrap.CheckBox} this This input
23041         * @param {Boolean} checked The new checked value
23042         */
23043        check : true,
23044        /**
23045         * @event click
23046         * Fires when the element is click.
23047         * @param {Roo.bootstrap.CheckBox} this This input
23048         */
23049        click : true
23050     });
23051     
23052 };
23053
23054 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23055   
23056     inputType: 'checkbox',
23057     inputValue: 1,
23058     valueOff: 0,
23059     boxLabel: false,
23060     checked: false,
23061     weight : false,
23062     inline: false,
23063     tooltip : '',
23064     
23065     // checkbox success does not make any sense really.. 
23066     invalidClass : "",
23067     validClass : "",
23068     
23069     
23070     getAutoCreate : function()
23071     {
23072         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23073         
23074         var id = Roo.id();
23075         
23076         var cfg = {};
23077         
23078         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23079         
23080         if(this.inline){
23081             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23082         }
23083         
23084         var input =  {
23085             tag: 'input',
23086             id : id,
23087             type : this.inputType,
23088             value : this.inputValue,
23089             cls : 'roo-' + this.inputType, //'form-box',
23090             placeholder : this.placeholder || ''
23091             
23092         };
23093         
23094         if(this.inputType != 'radio'){
23095             var hidden =  {
23096                 tag: 'input',
23097                 type : 'hidden',
23098                 cls : 'roo-hidden-value',
23099                 value : this.checked ? this.inputValue : this.valueOff
23100             };
23101         }
23102         
23103             
23104         if (this.weight) { // Validity check?
23105             cfg.cls += " " + this.inputType + "-" + this.weight;
23106         }
23107         
23108         if (this.disabled) {
23109             input.disabled=true;
23110         }
23111         
23112         if(this.checked){
23113             input.checked = this.checked;
23114         }
23115         
23116         if (this.name) {
23117             
23118             input.name = this.name;
23119             
23120             if(this.inputType != 'radio'){
23121                 hidden.name = this.name;
23122                 input.name = '_hidden_' + this.name;
23123             }
23124         }
23125         
23126         if (this.size) {
23127             input.cls += ' input-' + this.size;
23128         }
23129         
23130         var settings=this;
23131         
23132         ['xs','sm','md','lg'].map(function(size){
23133             if (settings[size]) {
23134                 cfg.cls += ' col-' + size + '-' + settings[size];
23135             }
23136         });
23137         
23138         var inputblock = input;
23139          
23140         if (this.before || this.after) {
23141             
23142             inputblock = {
23143                 cls : 'input-group',
23144                 cn :  [] 
23145             };
23146             
23147             if (this.before) {
23148                 inputblock.cn.push({
23149                     tag :'span',
23150                     cls : 'input-group-addon',
23151                     html : this.before
23152                 });
23153             }
23154             
23155             inputblock.cn.push(input);
23156             
23157             if(this.inputType != 'radio'){
23158                 inputblock.cn.push(hidden);
23159             }
23160             
23161             if (this.after) {
23162                 inputblock.cn.push({
23163                     tag :'span',
23164                     cls : 'input-group-addon',
23165                     html : this.after
23166                 });
23167             }
23168             
23169         }
23170         var boxLabelCfg = false;
23171         
23172         if(this.boxLabel){
23173            
23174             boxLabelCfg = {
23175                 tag: 'label',
23176                 //'for': id, // box label is handled by onclick - so no for...
23177                 cls: 'box-label',
23178                 html: this.boxLabel
23179             };
23180             if(this.tooltip){
23181                 boxLabelCfg.tooltip = this.tooltip;
23182             }
23183              
23184         }
23185         
23186         
23187         if (align ==='left' && this.fieldLabel.length) {
23188 //                Roo.log("left and has label");
23189             cfg.cn = [
23190                 {
23191                     tag: 'label',
23192                     'for' :  id,
23193                     cls : 'control-label',
23194                     html : this.fieldLabel
23195                 },
23196                 {
23197                     cls : "", 
23198                     cn: [
23199                         inputblock
23200                     ]
23201                 }
23202             ];
23203             
23204             if (boxLabelCfg) {
23205                 cfg.cn[1].cn.push(boxLabelCfg);
23206             }
23207             
23208             if(this.labelWidth > 12){
23209                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23210             }
23211             
23212             if(this.labelWidth < 13 && this.labelmd == 0){
23213                 this.labelmd = this.labelWidth;
23214             }
23215             
23216             if(this.labellg > 0){
23217                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23218                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23219             }
23220             
23221             if(this.labelmd > 0){
23222                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23223                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23224             }
23225             
23226             if(this.labelsm > 0){
23227                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23228                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23229             }
23230             
23231             if(this.labelxs > 0){
23232                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23233                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23234             }
23235             
23236         } else if ( this.fieldLabel.length) {
23237 //                Roo.log(" label");
23238                 cfg.cn = [
23239                    
23240                     {
23241                         tag: this.boxLabel ? 'span' : 'label',
23242                         'for': id,
23243                         cls: 'control-label box-input-label',
23244                         //cls : 'input-group-addon',
23245                         html : this.fieldLabel
23246                     },
23247                     
23248                     inputblock
23249                     
23250                 ];
23251                 if (boxLabelCfg) {
23252                     cfg.cn.push(boxLabelCfg);
23253                 }
23254
23255         } else {
23256             
23257 //                Roo.log(" no label && no align");
23258                 cfg.cn = [  inputblock ] ;
23259                 if (boxLabelCfg) {
23260                     cfg.cn.push(boxLabelCfg);
23261                 }
23262
23263                 
23264         }
23265         
23266        
23267         
23268         if(this.inputType != 'radio'){
23269             cfg.cn.push(hidden);
23270         }
23271         
23272         return cfg;
23273         
23274     },
23275     
23276     /**
23277      * return the real input element.
23278      */
23279     inputEl: function ()
23280     {
23281         return this.el.select('input.roo-' + this.inputType,true).first();
23282     },
23283     hiddenEl: function ()
23284     {
23285         return this.el.select('input.roo-hidden-value',true).first();
23286     },
23287     
23288     labelEl: function()
23289     {
23290         return this.el.select('label.control-label',true).first();
23291     },
23292     /* depricated... */
23293     
23294     label: function()
23295     {
23296         return this.labelEl();
23297     },
23298     
23299     boxLabelEl: function()
23300     {
23301         return this.el.select('label.box-label',true).first();
23302     },
23303     
23304     initEvents : function()
23305     {
23306 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23307         
23308         this.inputEl().on('click', this.onClick,  this);
23309         
23310         if (this.boxLabel) { 
23311             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23312         }
23313         
23314         this.startValue = this.getValue();
23315         
23316         if(this.groupId){
23317             Roo.bootstrap.CheckBox.register(this);
23318         }
23319     },
23320     
23321     onClick : function(e)
23322     {   
23323         if(this.fireEvent('click', this, e) !== false){
23324             this.setChecked(!this.checked);
23325         }
23326         
23327     },
23328     
23329     setChecked : function(state,suppressEvent)
23330     {
23331         this.startValue = this.getValue();
23332
23333         if(this.inputType == 'radio'){
23334             
23335             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23336                 e.dom.checked = false;
23337             });
23338             
23339             this.inputEl().dom.checked = true;
23340             
23341             this.inputEl().dom.value = this.inputValue;
23342             
23343             if(suppressEvent !== true){
23344                 this.fireEvent('check', this, true);
23345             }
23346             
23347             this.validate();
23348             
23349             return;
23350         }
23351         
23352         this.checked = state;
23353         
23354         this.inputEl().dom.checked = state;
23355         
23356         
23357         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23358         
23359         if(suppressEvent !== true){
23360             this.fireEvent('check', this, state);
23361         }
23362         
23363         this.validate();
23364     },
23365     
23366     getValue : function()
23367     {
23368         if(this.inputType == 'radio'){
23369             return this.getGroupValue();
23370         }
23371         
23372         return this.hiddenEl().dom.value;
23373         
23374     },
23375     
23376     getGroupValue : function()
23377     {
23378         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23379             return '';
23380         }
23381         
23382         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23383     },
23384     
23385     setValue : function(v,suppressEvent)
23386     {
23387         if(this.inputType == 'radio'){
23388             this.setGroupValue(v, suppressEvent);
23389             return;
23390         }
23391         
23392         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23393         
23394         this.validate();
23395     },
23396     
23397     setGroupValue : function(v, suppressEvent)
23398     {
23399         this.startValue = this.getValue();
23400         
23401         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23402             e.dom.checked = false;
23403             
23404             if(e.dom.value == v){
23405                 e.dom.checked = true;
23406             }
23407         });
23408         
23409         if(suppressEvent !== true){
23410             this.fireEvent('check', this, true);
23411         }
23412
23413         this.validate();
23414         
23415         return;
23416     },
23417     
23418     validate : function()
23419     {
23420         if(this.getVisibilityEl().hasClass('hidden')){
23421             return true;
23422         }
23423         
23424         if(
23425                 this.disabled || 
23426                 (this.inputType == 'radio' && this.validateRadio()) ||
23427                 (this.inputType == 'checkbox' && this.validateCheckbox())
23428         ){
23429             this.markValid();
23430             return true;
23431         }
23432         
23433         this.markInvalid();
23434         return false;
23435     },
23436     
23437     validateRadio : function()
23438     {
23439         if(this.getVisibilityEl().hasClass('hidden')){
23440             return true;
23441         }
23442         
23443         if(this.allowBlank){
23444             return true;
23445         }
23446         
23447         var valid = false;
23448         
23449         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23450             if(!e.dom.checked){
23451                 return;
23452             }
23453             
23454             valid = true;
23455             
23456             return false;
23457         });
23458         
23459         return valid;
23460     },
23461     
23462     validateCheckbox : function()
23463     {
23464         if(!this.groupId){
23465             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23466             //return (this.getValue() == this.inputValue) ? true : false;
23467         }
23468         
23469         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23470         
23471         if(!group){
23472             return false;
23473         }
23474         
23475         var r = false;
23476         
23477         for(var i in group){
23478             if(group[i].el.isVisible(true)){
23479                 r = false;
23480                 break;
23481             }
23482             
23483             r = true;
23484         }
23485         
23486         for(var i in group){
23487             if(r){
23488                 break;
23489             }
23490             
23491             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23492         }
23493         
23494         return r;
23495     },
23496     
23497     /**
23498      * Mark this field as valid
23499      */
23500     markValid : function()
23501     {
23502         var _this = this;
23503         
23504         this.fireEvent('valid', this);
23505         
23506         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23507         
23508         if(this.groupId){
23509             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23510         }
23511         
23512         if(label){
23513             label.markValid();
23514         }
23515
23516         if(this.inputType == 'radio'){
23517             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23518                 var fg = e.findParent('.form-group', false, true);
23519                 if (Roo.bootstrap.version == 3) {
23520                     fg.removeClass([_this.invalidClass, _this.validClass]);
23521                     fg.addClass(_this.validClass);
23522                 } else {
23523                     fg.removeClass(['is-valid', 'is-invalid']);
23524                     fg.addClass('is-valid');
23525                 }
23526             });
23527             
23528             return;
23529         }
23530
23531         if(!this.groupId){
23532             var fg = this.el.findParent('.form-group', false, true);
23533             if (Roo.bootstrap.version == 3) {
23534                 fg.removeClass([this.invalidClass, this.validClass]);
23535                 fg.addClass(this.validClass);
23536             } else {
23537                 fg.removeClass(['is-valid', 'is-invalid']);
23538                 fg.addClass('is-valid');
23539             }
23540             return;
23541         }
23542         
23543         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23544         
23545         if(!group){
23546             return;
23547         }
23548         
23549         for(var i in group){
23550             var fg = group[i].el.findParent('.form-group', false, true);
23551             if (Roo.bootstrap.version == 3) {
23552                 fg.removeClass([this.invalidClass, this.validClass]);
23553                 fg.addClass(this.validClass);
23554             } else {
23555                 fg.removeClass(['is-valid', 'is-invalid']);
23556                 fg.addClass('is-valid');
23557             }
23558         }
23559     },
23560     
23561      /**
23562      * Mark this field as invalid
23563      * @param {String} msg The validation message
23564      */
23565     markInvalid : function(msg)
23566     {
23567         if(this.allowBlank){
23568             return;
23569         }
23570         
23571         var _this = this;
23572         
23573         this.fireEvent('invalid', this, msg);
23574         
23575         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23576         
23577         if(this.groupId){
23578             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23579         }
23580         
23581         if(label){
23582             label.markInvalid();
23583         }
23584             
23585         if(this.inputType == 'radio'){
23586             
23587             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23588                 var fg = e.findParent('.form-group', false, true);
23589                 if (Roo.bootstrap.version == 3) {
23590                     fg.removeClass([_this.invalidClass, _this.validClass]);
23591                     fg.addClass(_this.invalidClass);
23592                 } else {
23593                     fg.removeClass(['is-invalid', 'is-valid']);
23594                     fg.addClass('is-invalid');
23595                 }
23596             });
23597             
23598             return;
23599         }
23600         
23601         if(!this.groupId){
23602             var fg = this.el.findParent('.form-group', false, true);
23603             if (Roo.bootstrap.version == 3) {
23604                 fg.removeClass([_this.invalidClass, _this.validClass]);
23605                 fg.addClass(_this.invalidClass);
23606             } else {
23607                 fg.removeClass(['is-invalid', 'is-valid']);
23608                 fg.addClass('is-invalid');
23609             }
23610             return;
23611         }
23612         
23613         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23614         
23615         if(!group){
23616             return;
23617         }
23618         
23619         for(var i in group){
23620             var fg = group[i].el.findParent('.form-group', false, true);
23621             if (Roo.bootstrap.version == 3) {
23622                 fg.removeClass([_this.invalidClass, _this.validClass]);
23623                 fg.addClass(_this.invalidClass);
23624             } else {
23625                 fg.removeClass(['is-invalid', 'is-valid']);
23626                 fg.addClass('is-invalid');
23627             }
23628         }
23629         
23630     },
23631     
23632     clearInvalid : function()
23633     {
23634         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23635         
23636         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23637         
23638         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23639         
23640         if (label && label.iconEl) {
23641             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23642             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23643         }
23644     },
23645     
23646     disable : function()
23647     {
23648         if(this.inputType != 'radio'){
23649             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23650             return;
23651         }
23652         
23653         var _this = this;
23654         
23655         if(this.rendered){
23656             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23657                 _this.getActionEl().addClass(this.disabledClass);
23658                 e.dom.disabled = true;
23659             });
23660         }
23661         
23662         this.disabled = true;
23663         this.fireEvent("disable", this);
23664         return this;
23665     },
23666
23667     enable : function()
23668     {
23669         if(this.inputType != 'radio'){
23670             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23671             return;
23672         }
23673         
23674         var _this = this;
23675         
23676         if(this.rendered){
23677             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23678                 _this.getActionEl().removeClass(this.disabledClass);
23679                 e.dom.disabled = false;
23680             });
23681         }
23682         
23683         this.disabled = false;
23684         this.fireEvent("enable", this);
23685         return this;
23686     },
23687     
23688     setBoxLabel : function(v)
23689     {
23690         this.boxLabel = v;
23691         
23692         if(this.rendered){
23693             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23694         }
23695     }
23696
23697 });
23698
23699 Roo.apply(Roo.bootstrap.CheckBox, {
23700     
23701     groups: {},
23702     
23703      /**
23704     * register a CheckBox Group
23705     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23706     */
23707     register : function(checkbox)
23708     {
23709         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23710             this.groups[checkbox.groupId] = {};
23711         }
23712         
23713         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23714             return;
23715         }
23716         
23717         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23718         
23719     },
23720     /**
23721     * fetch a CheckBox Group based on the group ID
23722     * @param {string} the group ID
23723     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23724     */
23725     get: function(groupId) {
23726         if (typeof(this.groups[groupId]) == 'undefined') {
23727             return false;
23728         }
23729         
23730         return this.groups[groupId] ;
23731     }
23732     
23733     
23734 });
23735 /*
23736  * - LGPL
23737  *
23738  * RadioItem
23739  * 
23740  */
23741
23742 /**
23743  * @class Roo.bootstrap.Radio
23744  * @extends Roo.bootstrap.Component
23745  * Bootstrap Radio class
23746  * @cfg {String} boxLabel - the label associated
23747  * @cfg {String} value - the value of radio
23748  * 
23749  * @constructor
23750  * Create a new Radio
23751  * @param {Object} config The config object
23752  */
23753 Roo.bootstrap.Radio = function(config){
23754     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23755     
23756 };
23757
23758 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23759     
23760     boxLabel : '',
23761     
23762     value : '',
23763     
23764     getAutoCreate : function()
23765     {
23766         var cfg = {
23767             tag : 'div',
23768             cls : 'form-group radio',
23769             cn : [
23770                 {
23771                     tag : 'label',
23772                     cls : 'box-label',
23773                     html : this.boxLabel
23774                 }
23775             ]
23776         };
23777         
23778         return cfg;
23779     },
23780     
23781     initEvents : function() 
23782     {
23783         this.parent().register(this);
23784         
23785         this.el.on('click', this.onClick, this);
23786         
23787     },
23788     
23789     onClick : function(e)
23790     {
23791         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23792             this.setChecked(true);
23793         }
23794     },
23795     
23796     setChecked : function(state, suppressEvent)
23797     {
23798         this.parent().setValue(this.value, suppressEvent);
23799         
23800     },
23801     
23802     setBoxLabel : function(v)
23803     {
23804         this.boxLabel = v;
23805         
23806         if(this.rendered){
23807             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23808         }
23809     }
23810     
23811 });
23812  
23813
23814  /*
23815  * - LGPL
23816  *
23817  * Input
23818  * 
23819  */
23820
23821 /**
23822  * @class Roo.bootstrap.SecurePass
23823  * @extends Roo.bootstrap.Input
23824  * Bootstrap SecurePass class
23825  *
23826  * 
23827  * @constructor
23828  * Create a new SecurePass
23829  * @param {Object} config The config object
23830  */
23831  
23832 Roo.bootstrap.SecurePass = function (config) {
23833     // these go here, so the translation tool can replace them..
23834     this.errors = {
23835         PwdEmpty: "Please type a password, and then retype it to confirm.",
23836         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23837         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23838         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23839         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23840         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23841         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23842         TooWeak: "Your password is Too Weak."
23843     },
23844     this.meterLabel = "Password strength:";
23845     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23846     this.meterClass = [
23847         "roo-password-meter-tooweak", 
23848         "roo-password-meter-weak", 
23849         "roo-password-meter-medium", 
23850         "roo-password-meter-strong", 
23851         "roo-password-meter-grey"
23852     ];
23853     
23854     this.errors = {};
23855     
23856     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23857 }
23858
23859 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23860     /**
23861      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23862      * {
23863      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23864      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23865      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23866      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23867      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23868      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23869      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23870      * })
23871      */
23872     // private
23873     
23874     meterWidth: 300,
23875     errorMsg :'',    
23876     errors: false,
23877     imageRoot: '/',
23878     /**
23879      * @cfg {String/Object} Label for the strength meter (defaults to
23880      * 'Password strength:')
23881      */
23882     // private
23883     meterLabel: '',
23884     /**
23885      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23886      * ['Weak', 'Medium', 'Strong'])
23887      */
23888     // private    
23889     pwdStrengths: false,    
23890     // private
23891     strength: 0,
23892     // private
23893     _lastPwd: null,
23894     // private
23895     kCapitalLetter: 0,
23896     kSmallLetter: 1,
23897     kDigit: 2,
23898     kPunctuation: 3,
23899     
23900     insecure: false,
23901     // private
23902     initEvents: function ()
23903     {
23904         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23905
23906         if (this.el.is('input[type=password]') && Roo.isSafari) {
23907             this.el.on('keydown', this.SafariOnKeyDown, this);
23908         }
23909
23910         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23911     },
23912     // private
23913     onRender: function (ct, position)
23914     {
23915         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23916         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23917         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23918
23919         this.trigger.createChild({
23920                    cn: [
23921                     {
23922                     //id: 'PwdMeter',
23923                     tag: 'div',
23924                     cls: 'roo-password-meter-grey col-xs-12',
23925                     style: {
23926                         //width: 0,
23927                         //width: this.meterWidth + 'px'                                                
23928                         }
23929                     },
23930                     {                            
23931                          cls: 'roo-password-meter-text'                          
23932                     }
23933                 ]            
23934         });
23935
23936          
23937         if (this.hideTrigger) {
23938             this.trigger.setDisplayed(false);
23939         }
23940         this.setSize(this.width || '', this.height || '');
23941     },
23942     // private
23943     onDestroy: function ()
23944     {
23945         if (this.trigger) {
23946             this.trigger.removeAllListeners();
23947             this.trigger.remove();
23948         }
23949         if (this.wrap) {
23950             this.wrap.remove();
23951         }
23952         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23953     },
23954     // private
23955     checkStrength: function ()
23956     {
23957         var pwd = this.inputEl().getValue();
23958         if (pwd == this._lastPwd) {
23959             return;
23960         }
23961
23962         var strength;
23963         if (this.ClientSideStrongPassword(pwd)) {
23964             strength = 3;
23965         } else if (this.ClientSideMediumPassword(pwd)) {
23966             strength = 2;
23967         } else if (this.ClientSideWeakPassword(pwd)) {
23968             strength = 1;
23969         } else {
23970             strength = 0;
23971         }
23972         
23973         Roo.log('strength1: ' + strength);
23974         
23975         //var pm = this.trigger.child('div/div/div').dom;
23976         var pm = this.trigger.child('div/div');
23977         pm.removeClass(this.meterClass);
23978         pm.addClass(this.meterClass[strength]);
23979                 
23980         
23981         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23982                 
23983         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23984         
23985         this._lastPwd = pwd;
23986     },
23987     reset: function ()
23988     {
23989         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23990         
23991         this._lastPwd = '';
23992         
23993         var pm = this.trigger.child('div/div');
23994         pm.removeClass(this.meterClass);
23995         pm.addClass('roo-password-meter-grey');        
23996         
23997         
23998         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23999         
24000         pt.innerHTML = '';
24001         this.inputEl().dom.type='password';
24002     },
24003     // private
24004     validateValue: function (value)
24005     {
24006         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24007             return false;
24008         }
24009         if (value.length == 0) {
24010             if (this.allowBlank) {
24011                 this.clearInvalid();
24012                 return true;
24013             }
24014
24015             this.markInvalid(this.errors.PwdEmpty);
24016             this.errorMsg = this.errors.PwdEmpty;
24017             return false;
24018         }
24019         
24020         if(this.insecure){
24021             return true;
24022         }
24023         
24024         if (!value.match(/[\x21-\x7e]+/)) {
24025             this.markInvalid(this.errors.PwdBadChar);
24026             this.errorMsg = this.errors.PwdBadChar;
24027             return false;
24028         }
24029         if (value.length < 6) {
24030             this.markInvalid(this.errors.PwdShort);
24031             this.errorMsg = this.errors.PwdShort;
24032             return false;
24033         }
24034         if (value.length > 16) {
24035             this.markInvalid(this.errors.PwdLong);
24036             this.errorMsg = this.errors.PwdLong;
24037             return false;
24038         }
24039         var strength;
24040         if (this.ClientSideStrongPassword(value)) {
24041             strength = 3;
24042         } else if (this.ClientSideMediumPassword(value)) {
24043             strength = 2;
24044         } else if (this.ClientSideWeakPassword(value)) {
24045             strength = 1;
24046         } else {
24047             strength = 0;
24048         }
24049
24050         
24051         if (strength < 2) {
24052             //this.markInvalid(this.errors.TooWeak);
24053             this.errorMsg = this.errors.TooWeak;
24054             //return false;
24055         }
24056         
24057         
24058         console.log('strength2: ' + strength);
24059         
24060         //var pm = this.trigger.child('div/div/div').dom;
24061         
24062         var pm = this.trigger.child('div/div');
24063         pm.removeClass(this.meterClass);
24064         pm.addClass(this.meterClass[strength]);
24065                 
24066         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24067                 
24068         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24069         
24070         this.errorMsg = ''; 
24071         return true;
24072     },
24073     // private
24074     CharacterSetChecks: function (type)
24075     {
24076         this.type = type;
24077         this.fResult = false;
24078     },
24079     // private
24080     isctype: function (character, type)
24081     {
24082         switch (type) {  
24083             case this.kCapitalLetter:
24084                 if (character >= 'A' && character <= 'Z') {
24085                     return true;
24086                 }
24087                 break;
24088             
24089             case this.kSmallLetter:
24090                 if (character >= 'a' && character <= 'z') {
24091                     return true;
24092                 }
24093                 break;
24094             
24095             case this.kDigit:
24096                 if (character >= '0' && character <= '9') {
24097                     return true;
24098                 }
24099                 break;
24100             
24101             case this.kPunctuation:
24102                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24103                     return true;
24104                 }
24105                 break;
24106             
24107             default:
24108                 return false;
24109         }
24110
24111     },
24112     // private
24113     IsLongEnough: function (pwd, size)
24114     {
24115         return !(pwd == null || isNaN(size) || pwd.length < size);
24116     },
24117     // private
24118     SpansEnoughCharacterSets: function (word, nb)
24119     {
24120         if (!this.IsLongEnough(word, nb))
24121         {
24122             return false;
24123         }
24124
24125         var characterSetChecks = new Array(
24126             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24127             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24128         );
24129         
24130         for (var index = 0; index < word.length; ++index) {
24131             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24132                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24133                     characterSetChecks[nCharSet].fResult = true;
24134                     break;
24135                 }
24136             }
24137         }
24138
24139         var nCharSets = 0;
24140         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24141             if (characterSetChecks[nCharSet].fResult) {
24142                 ++nCharSets;
24143             }
24144         }
24145
24146         if (nCharSets < nb) {
24147             return false;
24148         }
24149         return true;
24150     },
24151     // private
24152     ClientSideStrongPassword: function (pwd)
24153     {
24154         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24155     },
24156     // private
24157     ClientSideMediumPassword: function (pwd)
24158     {
24159         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24160     },
24161     // private
24162     ClientSideWeakPassword: function (pwd)
24163     {
24164         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24165     }
24166           
24167 })//<script type="text/javascript">
24168
24169 /*
24170  * Based  Ext JS Library 1.1.1
24171  * Copyright(c) 2006-2007, Ext JS, LLC.
24172  * LGPL
24173  *
24174  */
24175  
24176 /**
24177  * @class Roo.HtmlEditorCore
24178  * @extends Roo.Component
24179  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24180  *
24181  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24182  */
24183
24184 Roo.HtmlEditorCore = function(config){
24185     
24186     
24187     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24188     
24189     
24190     this.addEvents({
24191         /**
24192          * @event initialize
24193          * Fires when the editor is fully initialized (including the iframe)
24194          * @param {Roo.HtmlEditorCore} this
24195          */
24196         initialize: true,
24197         /**
24198          * @event activate
24199          * Fires when the editor is first receives the focus. Any insertion must wait
24200          * until after this event.
24201          * @param {Roo.HtmlEditorCore} this
24202          */
24203         activate: true,
24204          /**
24205          * @event beforesync
24206          * Fires before the textarea is updated with content from the editor iframe. Return false
24207          * to cancel the sync.
24208          * @param {Roo.HtmlEditorCore} this
24209          * @param {String} html
24210          */
24211         beforesync: true,
24212          /**
24213          * @event beforepush
24214          * Fires before the iframe editor is updated with content from the textarea. Return false
24215          * to cancel the push.
24216          * @param {Roo.HtmlEditorCore} this
24217          * @param {String} html
24218          */
24219         beforepush: true,
24220          /**
24221          * @event sync
24222          * Fires when the textarea is updated with content from the editor iframe.
24223          * @param {Roo.HtmlEditorCore} this
24224          * @param {String} html
24225          */
24226         sync: true,
24227          /**
24228          * @event push
24229          * Fires when the iframe editor is updated with content from the textarea.
24230          * @param {Roo.HtmlEditorCore} this
24231          * @param {String} html
24232          */
24233         push: true,
24234         
24235         /**
24236          * @event editorevent
24237          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24238          * @param {Roo.HtmlEditorCore} this
24239          */
24240         editorevent: true
24241         
24242     });
24243     
24244     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24245     
24246     // defaults : white / black...
24247     this.applyBlacklists();
24248     
24249     
24250     
24251 };
24252
24253
24254 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24255
24256
24257      /**
24258      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24259      */
24260     
24261     owner : false,
24262     
24263      /**
24264      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24265      *                        Roo.resizable.
24266      */
24267     resizable : false,
24268      /**
24269      * @cfg {Number} height (in pixels)
24270      */   
24271     height: 300,
24272    /**
24273      * @cfg {Number} width (in pixels)
24274      */   
24275     width: 500,
24276     
24277     /**
24278      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24279      * 
24280      */
24281     stylesheets: false,
24282     
24283     // id of frame..
24284     frameId: false,
24285     
24286     // private properties
24287     validationEvent : false,
24288     deferHeight: true,
24289     initialized : false,
24290     activated : false,
24291     sourceEditMode : false,
24292     onFocus : Roo.emptyFn,
24293     iframePad:3,
24294     hideMode:'offsets',
24295     
24296     clearUp: true,
24297     
24298     // blacklist + whitelisted elements..
24299     black: false,
24300     white: false,
24301      
24302     bodyCls : '',
24303
24304     /**
24305      * Protected method that will not generally be called directly. It
24306      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24307      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24308      */
24309     getDocMarkup : function(){
24310         // body styles..
24311         var st = '';
24312         
24313         // inherit styels from page...?? 
24314         if (this.stylesheets === false) {
24315             
24316             Roo.get(document.head).select('style').each(function(node) {
24317                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24318             });
24319             
24320             Roo.get(document.head).select('link').each(function(node) { 
24321                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24322             });
24323             
24324         } else if (!this.stylesheets.length) {
24325                 // simple..
24326                 st = '<style type="text/css">' +
24327                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24328                    '</style>';
24329         } else {
24330             for (var i in this.stylesheets) { 
24331                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24332             }
24333             
24334         }
24335         
24336         st +=  '<style type="text/css">' +
24337             'IMG { cursor: pointer } ' +
24338         '</style>';
24339
24340         var cls = 'roo-htmleditor-body';
24341         
24342         if(this.bodyCls.length){
24343             cls += ' ' + this.bodyCls;
24344         }
24345         
24346         return '<html><head>' + st  +
24347             //<style type="text/css">' +
24348             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24349             //'</style>' +
24350             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24351     },
24352
24353     // private
24354     onRender : function(ct, position)
24355     {
24356         var _t = this;
24357         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24358         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24359         
24360         
24361         this.el.dom.style.border = '0 none';
24362         this.el.dom.setAttribute('tabIndex', -1);
24363         this.el.addClass('x-hidden hide');
24364         
24365         
24366         
24367         if(Roo.isIE){ // fix IE 1px bogus margin
24368             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24369         }
24370        
24371         
24372         this.frameId = Roo.id();
24373         
24374          
24375         
24376         var iframe = this.owner.wrap.createChild({
24377             tag: 'iframe',
24378             cls: 'form-control', // bootstrap..
24379             id: this.frameId,
24380             name: this.frameId,
24381             frameBorder : 'no',
24382             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24383         }, this.el
24384         );
24385         
24386         
24387         this.iframe = iframe.dom;
24388
24389          this.assignDocWin();
24390         
24391         this.doc.designMode = 'on';
24392        
24393         this.doc.open();
24394         this.doc.write(this.getDocMarkup());
24395         this.doc.close();
24396
24397         
24398         var task = { // must defer to wait for browser to be ready
24399             run : function(){
24400                 //console.log("run task?" + this.doc.readyState);
24401                 this.assignDocWin();
24402                 if(this.doc.body || this.doc.readyState == 'complete'){
24403                     try {
24404                         this.doc.designMode="on";
24405                     } catch (e) {
24406                         return;
24407                     }
24408                     Roo.TaskMgr.stop(task);
24409                     this.initEditor.defer(10, this);
24410                 }
24411             },
24412             interval : 10,
24413             duration: 10000,
24414             scope: this
24415         };
24416         Roo.TaskMgr.start(task);
24417
24418     },
24419
24420     // private
24421     onResize : function(w, h)
24422     {
24423          Roo.log('resize: ' +w + ',' + h );
24424         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24425         if(!this.iframe){
24426             return;
24427         }
24428         if(typeof w == 'number'){
24429             
24430             this.iframe.style.width = w + 'px';
24431         }
24432         if(typeof h == 'number'){
24433             
24434             this.iframe.style.height = h + 'px';
24435             if(this.doc){
24436                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24437             }
24438         }
24439         
24440     },
24441
24442     /**
24443      * Toggles the editor between standard and source edit mode.
24444      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24445      */
24446     toggleSourceEdit : function(sourceEditMode){
24447         
24448         this.sourceEditMode = sourceEditMode === true;
24449         
24450         if(this.sourceEditMode){
24451  
24452             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24453             
24454         }else{
24455             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24456             //this.iframe.className = '';
24457             this.deferFocus();
24458         }
24459         //this.setSize(this.owner.wrap.getSize());
24460         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24461     },
24462
24463     
24464   
24465
24466     /**
24467      * Protected method that will not generally be called directly. If you need/want
24468      * custom HTML cleanup, this is the method you should override.
24469      * @param {String} html The HTML to be cleaned
24470      * return {String} The cleaned HTML
24471      */
24472     cleanHtml : function(html){
24473         html = String(html);
24474         if(html.length > 5){
24475             if(Roo.isSafari){ // strip safari nonsense
24476                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24477             }
24478         }
24479         if(html == '&nbsp;'){
24480             html = '';
24481         }
24482         return html;
24483     },
24484
24485     /**
24486      * HTML Editor -> Textarea
24487      * Protected method that will not generally be called directly. Syncs the contents
24488      * of the editor iframe with the textarea.
24489      */
24490     syncValue : function(){
24491         if(this.initialized){
24492             var bd = (this.doc.body || this.doc.documentElement);
24493             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24494             var html = bd.innerHTML;
24495             if(Roo.isSafari){
24496                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24497                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24498                 if(m && m[1]){
24499                     html = '<div style="'+m[0]+'">' + html + '</div>';
24500                 }
24501             }
24502             html = this.cleanHtml(html);
24503             // fix up the special chars.. normaly like back quotes in word...
24504             // however we do not want to do this with chinese..
24505             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24506                 
24507                 var cc = match.charCodeAt();
24508
24509                 // Get the character value, handling surrogate pairs
24510                 if (match.length == 2) {
24511                     // It's a surrogate pair, calculate the Unicode code point
24512                     var high = match.charCodeAt(0) - 0xD800;
24513                     var low  = match.charCodeAt(1) - 0xDC00;
24514                     cc = (high * 0x400) + low + 0x10000;
24515                 }  else if (
24516                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24517                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24518                     (cc >= 0xf900 && cc < 0xfb00 )
24519                 ) {
24520                         return match;
24521                 }  
24522          
24523                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24524                 return "&#" + cc + ";";
24525                 
24526                 
24527             });
24528             
24529             
24530              
24531             if(this.owner.fireEvent('beforesync', this, html) !== false){
24532                 this.el.dom.value = html;
24533                 this.owner.fireEvent('sync', this, html);
24534             }
24535         }
24536     },
24537
24538     /**
24539      * Protected method that will not generally be called directly. Pushes the value of the textarea
24540      * into the iframe editor.
24541      */
24542     pushValue : function(){
24543         if(this.initialized){
24544             var v = this.el.dom.value.trim();
24545             
24546 //            if(v.length < 1){
24547 //                v = '&#160;';
24548 //            }
24549             
24550             if(this.owner.fireEvent('beforepush', this, v) !== false){
24551                 var d = (this.doc.body || this.doc.documentElement);
24552                 d.innerHTML = v;
24553                 this.cleanUpPaste();
24554                 this.el.dom.value = d.innerHTML;
24555                 this.owner.fireEvent('push', this, v);
24556             }
24557         }
24558     },
24559
24560     // private
24561     deferFocus : function(){
24562         this.focus.defer(10, this);
24563     },
24564
24565     // doc'ed in Field
24566     focus : function(){
24567         if(this.win && !this.sourceEditMode){
24568             this.win.focus();
24569         }else{
24570             this.el.focus();
24571         }
24572     },
24573     
24574     assignDocWin: function()
24575     {
24576         var iframe = this.iframe;
24577         
24578          if(Roo.isIE){
24579             this.doc = iframe.contentWindow.document;
24580             this.win = iframe.contentWindow;
24581         } else {
24582 //            if (!Roo.get(this.frameId)) {
24583 //                return;
24584 //            }
24585 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24586 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24587             
24588             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24589                 return;
24590             }
24591             
24592             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24593             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24594         }
24595     },
24596     
24597     // private
24598     initEditor : function(){
24599         //console.log("INIT EDITOR");
24600         this.assignDocWin();
24601         
24602         
24603         
24604         this.doc.designMode="on";
24605         this.doc.open();
24606         this.doc.write(this.getDocMarkup());
24607         this.doc.close();
24608         
24609         var dbody = (this.doc.body || this.doc.documentElement);
24610         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24611         // this copies styles from the containing element into thsi one..
24612         // not sure why we need all of this..
24613         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24614         
24615         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24616         //ss['background-attachment'] = 'fixed'; // w3c
24617         dbody.bgProperties = 'fixed'; // ie
24618         //Roo.DomHelper.applyStyles(dbody, ss);
24619         Roo.EventManager.on(this.doc, {
24620             //'mousedown': this.onEditorEvent,
24621             'mouseup': this.onEditorEvent,
24622             'dblclick': this.onEditorEvent,
24623             'click': this.onEditorEvent,
24624             'keyup': this.onEditorEvent,
24625             buffer:100,
24626             scope: this
24627         });
24628         if(Roo.isGecko){
24629             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24630         }
24631         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24632             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24633         }
24634         this.initialized = true;
24635
24636         this.owner.fireEvent('initialize', this);
24637         this.pushValue();
24638     },
24639
24640     // private
24641     onDestroy : function(){
24642         
24643         
24644         
24645         if(this.rendered){
24646             
24647             //for (var i =0; i < this.toolbars.length;i++) {
24648             //    // fixme - ask toolbars for heights?
24649             //    this.toolbars[i].onDestroy();
24650            // }
24651             
24652             //this.wrap.dom.innerHTML = '';
24653             //this.wrap.remove();
24654         }
24655     },
24656
24657     // private
24658     onFirstFocus : function(){
24659         
24660         this.assignDocWin();
24661         
24662         
24663         this.activated = true;
24664          
24665     
24666         if(Roo.isGecko){ // prevent silly gecko errors
24667             this.win.focus();
24668             var s = this.win.getSelection();
24669             if(!s.focusNode || s.focusNode.nodeType != 3){
24670                 var r = s.getRangeAt(0);
24671                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24672                 r.collapse(true);
24673                 this.deferFocus();
24674             }
24675             try{
24676                 this.execCmd('useCSS', true);
24677                 this.execCmd('styleWithCSS', false);
24678             }catch(e){}
24679         }
24680         this.owner.fireEvent('activate', this);
24681     },
24682
24683     // private
24684     adjustFont: function(btn){
24685         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24686         //if(Roo.isSafari){ // safari
24687         //    adjust *= 2;
24688        // }
24689         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24690         if(Roo.isSafari){ // safari
24691             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24692             v =  (v < 10) ? 10 : v;
24693             v =  (v > 48) ? 48 : v;
24694             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24695             
24696         }
24697         
24698         
24699         v = Math.max(1, v+adjust);
24700         
24701         this.execCmd('FontSize', v  );
24702     },
24703
24704     onEditorEvent : function(e)
24705     {
24706         this.owner.fireEvent('editorevent', this, e);
24707       //  this.updateToolbar();
24708         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24709     },
24710
24711     insertTag : function(tg)
24712     {
24713         // could be a bit smarter... -> wrap the current selected tRoo..
24714         if (tg.toLowerCase() == 'span' ||
24715             tg.toLowerCase() == 'code' ||
24716             tg.toLowerCase() == 'sup' ||
24717             tg.toLowerCase() == 'sub' 
24718             ) {
24719             
24720             range = this.createRange(this.getSelection());
24721             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24722             wrappingNode.appendChild(range.extractContents());
24723             range.insertNode(wrappingNode);
24724
24725             return;
24726             
24727             
24728             
24729         }
24730         this.execCmd("formatblock",   tg);
24731         
24732     },
24733     
24734     insertText : function(txt)
24735     {
24736         
24737         
24738         var range = this.createRange();
24739         range.deleteContents();
24740                //alert(Sender.getAttribute('label'));
24741                
24742         range.insertNode(this.doc.createTextNode(txt));
24743     } ,
24744     
24745      
24746
24747     /**
24748      * Executes a Midas editor command on the editor document and performs necessary focus and
24749      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24750      * @param {String} cmd The Midas command
24751      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24752      */
24753     relayCmd : function(cmd, value){
24754         this.win.focus();
24755         this.execCmd(cmd, value);
24756         this.owner.fireEvent('editorevent', this);
24757         //this.updateToolbar();
24758         this.owner.deferFocus();
24759     },
24760
24761     /**
24762      * Executes a Midas editor command directly on the editor document.
24763      * For visual commands, you should use {@link #relayCmd} instead.
24764      * <b>This should only be called after the editor is initialized.</b>
24765      * @param {String} cmd The Midas command
24766      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24767      */
24768     execCmd : function(cmd, value){
24769         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24770         this.syncValue();
24771     },
24772  
24773  
24774    
24775     /**
24776      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24777      * to insert tRoo.
24778      * @param {String} text | dom node.. 
24779      */
24780     insertAtCursor : function(text)
24781     {
24782         
24783         if(!this.activated){
24784             return;
24785         }
24786         /*
24787         if(Roo.isIE){
24788             this.win.focus();
24789             var r = this.doc.selection.createRange();
24790             if(r){
24791                 r.collapse(true);
24792                 r.pasteHTML(text);
24793                 this.syncValue();
24794                 this.deferFocus();
24795             
24796             }
24797             return;
24798         }
24799         */
24800         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24801             this.win.focus();
24802             
24803             
24804             // from jquery ui (MIT licenced)
24805             var range, node;
24806             var win = this.win;
24807             
24808             if (win.getSelection && win.getSelection().getRangeAt) {
24809                 range = win.getSelection().getRangeAt(0);
24810                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24811                 range.insertNode(node);
24812             } else if (win.document.selection && win.document.selection.createRange) {
24813                 // no firefox support
24814                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24815                 win.document.selection.createRange().pasteHTML(txt);
24816             } else {
24817                 // no firefox support
24818                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24819                 this.execCmd('InsertHTML', txt);
24820             } 
24821             
24822             this.syncValue();
24823             
24824             this.deferFocus();
24825         }
24826     },
24827  // private
24828     mozKeyPress : function(e){
24829         if(e.ctrlKey){
24830             var c = e.getCharCode(), cmd;
24831           
24832             if(c > 0){
24833                 c = String.fromCharCode(c).toLowerCase();
24834                 switch(c){
24835                     case 'b':
24836                         cmd = 'bold';
24837                         break;
24838                     case 'i':
24839                         cmd = 'italic';
24840                         break;
24841                     
24842                     case 'u':
24843                         cmd = 'underline';
24844                         break;
24845                     
24846                     case 'v':
24847                         this.cleanUpPaste.defer(100, this);
24848                         return;
24849                         
24850                 }
24851                 if(cmd){
24852                     this.win.focus();
24853                     this.execCmd(cmd);
24854                     this.deferFocus();
24855                     e.preventDefault();
24856                 }
24857                 
24858             }
24859         }
24860     },
24861
24862     // private
24863     fixKeys : function(){ // load time branching for fastest keydown performance
24864         if(Roo.isIE){
24865             return function(e){
24866                 var k = e.getKey(), r;
24867                 if(k == e.TAB){
24868                     e.stopEvent();
24869                     r = this.doc.selection.createRange();
24870                     if(r){
24871                         r.collapse(true);
24872                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24873                         this.deferFocus();
24874                     }
24875                     return;
24876                 }
24877                 
24878                 if(k == e.ENTER){
24879                     r = this.doc.selection.createRange();
24880                     if(r){
24881                         var target = r.parentElement();
24882                         if(!target || target.tagName.toLowerCase() != 'li'){
24883                             e.stopEvent();
24884                             r.pasteHTML('<br />');
24885                             r.collapse(false);
24886                             r.select();
24887                         }
24888                     }
24889                 }
24890                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24891                     this.cleanUpPaste.defer(100, this);
24892                     return;
24893                 }
24894                 
24895                 
24896             };
24897         }else if(Roo.isOpera){
24898             return function(e){
24899                 var k = e.getKey();
24900                 if(k == e.TAB){
24901                     e.stopEvent();
24902                     this.win.focus();
24903                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24904                     this.deferFocus();
24905                 }
24906                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24907                     this.cleanUpPaste.defer(100, this);
24908                     return;
24909                 }
24910                 
24911             };
24912         }else if(Roo.isSafari){
24913             return function(e){
24914                 var k = e.getKey();
24915                 
24916                 if(k == e.TAB){
24917                     e.stopEvent();
24918                     this.execCmd('InsertText','\t');
24919                     this.deferFocus();
24920                     return;
24921                 }
24922                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24923                     this.cleanUpPaste.defer(100, this);
24924                     return;
24925                 }
24926                 
24927              };
24928         }
24929     }(),
24930     
24931     getAllAncestors: function()
24932     {
24933         var p = this.getSelectedNode();
24934         var a = [];
24935         if (!p) {
24936             a.push(p); // push blank onto stack..
24937             p = this.getParentElement();
24938         }
24939         
24940         
24941         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24942             a.push(p);
24943             p = p.parentNode;
24944         }
24945         a.push(this.doc.body);
24946         return a;
24947     },
24948     lastSel : false,
24949     lastSelNode : false,
24950     
24951     
24952     getSelection : function() 
24953     {
24954         this.assignDocWin();
24955         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24956     },
24957     
24958     getSelectedNode: function() 
24959     {
24960         // this may only work on Gecko!!!
24961         
24962         // should we cache this!!!!
24963         
24964         
24965         
24966          
24967         var range = this.createRange(this.getSelection()).cloneRange();
24968         
24969         if (Roo.isIE) {
24970             var parent = range.parentElement();
24971             while (true) {
24972                 var testRange = range.duplicate();
24973                 testRange.moveToElementText(parent);
24974                 if (testRange.inRange(range)) {
24975                     break;
24976                 }
24977                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24978                     break;
24979                 }
24980                 parent = parent.parentElement;
24981             }
24982             return parent;
24983         }
24984         
24985         // is ancestor a text element.
24986         var ac =  range.commonAncestorContainer;
24987         if (ac.nodeType == 3) {
24988             ac = ac.parentNode;
24989         }
24990         
24991         var ar = ac.childNodes;
24992          
24993         var nodes = [];
24994         var other_nodes = [];
24995         var has_other_nodes = false;
24996         for (var i=0;i<ar.length;i++) {
24997             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24998                 continue;
24999             }
25000             // fullly contained node.
25001             
25002             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25003                 nodes.push(ar[i]);
25004                 continue;
25005             }
25006             
25007             // probably selected..
25008             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25009                 other_nodes.push(ar[i]);
25010                 continue;
25011             }
25012             // outer..
25013             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25014                 continue;
25015             }
25016             
25017             
25018             has_other_nodes = true;
25019         }
25020         if (!nodes.length && other_nodes.length) {
25021             nodes= other_nodes;
25022         }
25023         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25024             return false;
25025         }
25026         
25027         return nodes[0];
25028     },
25029     createRange: function(sel)
25030     {
25031         // this has strange effects when using with 
25032         // top toolbar - not sure if it's a great idea.
25033         //this.editor.contentWindow.focus();
25034         if (typeof sel != "undefined") {
25035             try {
25036                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25037             } catch(e) {
25038                 return this.doc.createRange();
25039             }
25040         } else {
25041             return this.doc.createRange();
25042         }
25043     },
25044     getParentElement: function()
25045     {
25046         
25047         this.assignDocWin();
25048         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25049         
25050         var range = this.createRange(sel);
25051          
25052         try {
25053             var p = range.commonAncestorContainer;
25054             while (p.nodeType == 3) { // text node
25055                 p = p.parentNode;
25056             }
25057             return p;
25058         } catch (e) {
25059             return null;
25060         }
25061     
25062     },
25063     /***
25064      *
25065      * Range intersection.. the hard stuff...
25066      *  '-1' = before
25067      *  '0' = hits..
25068      *  '1' = after.
25069      *         [ -- selected range --- ]
25070      *   [fail]                        [fail]
25071      *
25072      *    basically..
25073      *      if end is before start or  hits it. fail.
25074      *      if start is after end or hits it fail.
25075      *
25076      *   if either hits (but other is outside. - then it's not 
25077      *   
25078      *    
25079      **/
25080     
25081     
25082     // @see http://www.thismuchiknow.co.uk/?p=64.
25083     rangeIntersectsNode : function(range, node)
25084     {
25085         var nodeRange = node.ownerDocument.createRange();
25086         try {
25087             nodeRange.selectNode(node);
25088         } catch (e) {
25089             nodeRange.selectNodeContents(node);
25090         }
25091     
25092         var rangeStartRange = range.cloneRange();
25093         rangeStartRange.collapse(true);
25094     
25095         var rangeEndRange = range.cloneRange();
25096         rangeEndRange.collapse(false);
25097     
25098         var nodeStartRange = nodeRange.cloneRange();
25099         nodeStartRange.collapse(true);
25100     
25101         var nodeEndRange = nodeRange.cloneRange();
25102         nodeEndRange.collapse(false);
25103     
25104         return rangeStartRange.compareBoundaryPoints(
25105                  Range.START_TO_START, nodeEndRange) == -1 &&
25106                rangeEndRange.compareBoundaryPoints(
25107                  Range.START_TO_START, nodeStartRange) == 1;
25108         
25109          
25110     },
25111     rangeCompareNode : function(range, node)
25112     {
25113         var nodeRange = node.ownerDocument.createRange();
25114         try {
25115             nodeRange.selectNode(node);
25116         } catch (e) {
25117             nodeRange.selectNodeContents(node);
25118         }
25119         
25120         
25121         range.collapse(true);
25122     
25123         nodeRange.collapse(true);
25124      
25125         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25126         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25127          
25128         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25129         
25130         var nodeIsBefore   =  ss == 1;
25131         var nodeIsAfter    = ee == -1;
25132         
25133         if (nodeIsBefore && nodeIsAfter) {
25134             return 0; // outer
25135         }
25136         if (!nodeIsBefore && nodeIsAfter) {
25137             return 1; //right trailed.
25138         }
25139         
25140         if (nodeIsBefore && !nodeIsAfter) {
25141             return 2;  // left trailed.
25142         }
25143         // fully contined.
25144         return 3;
25145     },
25146
25147     // private? - in a new class?
25148     cleanUpPaste :  function()
25149     {
25150         // cleans up the whole document..
25151         Roo.log('cleanuppaste');
25152         
25153         this.cleanUpChildren(this.doc.body);
25154         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25155         if (clean != this.doc.body.innerHTML) {
25156             this.doc.body.innerHTML = clean;
25157         }
25158         
25159     },
25160     
25161     cleanWordChars : function(input) {// change the chars to hex code
25162         var he = Roo.HtmlEditorCore;
25163         
25164         var output = input;
25165         Roo.each(he.swapCodes, function(sw) { 
25166             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25167             
25168             output = output.replace(swapper, sw[1]);
25169         });
25170         
25171         return output;
25172     },
25173     
25174     
25175     cleanUpChildren : function (n)
25176     {
25177         if (!n.childNodes.length) {
25178             return;
25179         }
25180         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25181            this.cleanUpChild(n.childNodes[i]);
25182         }
25183     },
25184     
25185     
25186         
25187     
25188     cleanUpChild : function (node)
25189     {
25190         var ed = this;
25191         //console.log(node);
25192         if (node.nodeName == "#text") {
25193             // clean up silly Windows -- stuff?
25194             return; 
25195         }
25196         if (node.nodeName == "#comment") {
25197             node.parentNode.removeChild(node);
25198             // clean up silly Windows -- stuff?
25199             return; 
25200         }
25201         var lcname = node.tagName.toLowerCase();
25202         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25203         // whitelist of tags..
25204         
25205         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25206             // remove node.
25207             node.parentNode.removeChild(node);
25208             return;
25209             
25210         }
25211         
25212         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25213         
25214         // spans with no attributes - just remove them..
25215         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25216             remove_keep_children = true;
25217         }
25218         
25219         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25220         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25221         
25222         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25223         //    remove_keep_children = true;
25224         //}
25225         
25226         if (remove_keep_children) {
25227             this.cleanUpChildren(node);
25228             // inserts everything just before this node...
25229             while (node.childNodes.length) {
25230                 var cn = node.childNodes[0];
25231                 node.removeChild(cn);
25232                 node.parentNode.insertBefore(cn, node);
25233             }
25234             node.parentNode.removeChild(node);
25235             return;
25236         }
25237         
25238         if (!node.attributes || !node.attributes.length) {
25239             
25240           
25241             
25242             
25243             this.cleanUpChildren(node);
25244             return;
25245         }
25246         
25247         function cleanAttr(n,v)
25248         {
25249             
25250             if (v.match(/^\./) || v.match(/^\//)) {
25251                 return;
25252             }
25253             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25254                 return;
25255             }
25256             if (v.match(/^#/)) {
25257                 return;
25258             }
25259             if (v.match(/^\{/)) { // allow template editing.
25260                 return;
25261             }
25262 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25263             node.removeAttribute(n);
25264             
25265         }
25266         
25267         var cwhite = this.cwhite;
25268         var cblack = this.cblack;
25269             
25270         function cleanStyle(n,v)
25271         {
25272             if (v.match(/expression/)) { //XSS?? should we even bother..
25273                 node.removeAttribute(n);
25274                 return;
25275             }
25276             
25277             var parts = v.split(/;/);
25278             var clean = [];
25279             
25280             Roo.each(parts, function(p) {
25281                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25282                 if (!p.length) {
25283                     return true;
25284                 }
25285                 var l = p.split(':').shift().replace(/\s+/g,'');
25286                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25287                 
25288                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25289 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25290                     //node.removeAttribute(n);
25291                     return true;
25292                 }
25293                 //Roo.log()
25294                 // only allow 'c whitelisted system attributes'
25295                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25296 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25297                     //node.removeAttribute(n);
25298                     return true;
25299                 }
25300                 
25301                 
25302                  
25303                 
25304                 clean.push(p);
25305                 return true;
25306             });
25307             if (clean.length) { 
25308                 node.setAttribute(n, clean.join(';'));
25309             } else {
25310                 node.removeAttribute(n);
25311             }
25312             
25313         }
25314         
25315         
25316         for (var i = node.attributes.length-1; i > -1 ; i--) {
25317             var a = node.attributes[i];
25318             //console.log(a);
25319             
25320             if (a.name.toLowerCase().substr(0,2)=='on')  {
25321                 node.removeAttribute(a.name);
25322                 continue;
25323             }
25324             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25325                 node.removeAttribute(a.name);
25326                 continue;
25327             }
25328             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25329                 cleanAttr(a.name,a.value); // fixme..
25330                 continue;
25331             }
25332             if (a.name == 'style') {
25333                 cleanStyle(a.name,a.value);
25334                 continue;
25335             }
25336             /// clean up MS crap..
25337             // tecnically this should be a list of valid class'es..
25338             
25339             
25340             if (a.name == 'class') {
25341                 if (a.value.match(/^Mso/)) {
25342                     node.removeAttribute('class');
25343                 }
25344                 
25345                 if (a.value.match(/^body$/)) {
25346                     node.removeAttribute('class');
25347                 }
25348                 continue;
25349             }
25350             
25351             // style cleanup!?
25352             // class cleanup?
25353             
25354         }
25355         
25356         
25357         this.cleanUpChildren(node);
25358         
25359         
25360     },
25361     
25362     /**
25363      * Clean up MS wordisms...
25364      */
25365     cleanWord : function(node)
25366     {
25367         if (!node) {
25368             this.cleanWord(this.doc.body);
25369             return;
25370         }
25371         
25372         if(
25373                 node.nodeName == 'SPAN' &&
25374                 !node.hasAttributes() &&
25375                 node.childNodes.length == 1 &&
25376                 node.firstChild.nodeName == "#text"  
25377         ) {
25378             var textNode = node.firstChild;
25379             node.removeChild(textNode);
25380             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25381                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25382             }
25383             node.parentNode.insertBefore(textNode, node);
25384             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25385                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25386             }
25387             node.parentNode.removeChild(node);
25388         }
25389         
25390         if (node.nodeName == "#text") {
25391             // clean up silly Windows -- stuff?
25392             return; 
25393         }
25394         if (node.nodeName == "#comment") {
25395             node.parentNode.removeChild(node);
25396             // clean up silly Windows -- stuff?
25397             return; 
25398         }
25399         
25400         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25401             node.parentNode.removeChild(node);
25402             return;
25403         }
25404         //Roo.log(node.tagName);
25405         // remove - but keep children..
25406         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25407             //Roo.log('-- removed');
25408             while (node.childNodes.length) {
25409                 var cn = node.childNodes[0];
25410                 node.removeChild(cn);
25411                 node.parentNode.insertBefore(cn, node);
25412                 // move node to parent - and clean it..
25413                 this.cleanWord(cn);
25414             }
25415             node.parentNode.removeChild(node);
25416             /// no need to iterate chidlren = it's got none..
25417             //this.iterateChildren(node, this.cleanWord);
25418             return;
25419         }
25420         // clean styles
25421         if (node.className.length) {
25422             
25423             var cn = node.className.split(/\W+/);
25424             var cna = [];
25425             Roo.each(cn, function(cls) {
25426                 if (cls.match(/Mso[a-zA-Z]+/)) {
25427                     return;
25428                 }
25429                 cna.push(cls);
25430             });
25431             node.className = cna.length ? cna.join(' ') : '';
25432             if (!cna.length) {
25433                 node.removeAttribute("class");
25434             }
25435         }
25436         
25437         if (node.hasAttribute("lang")) {
25438             node.removeAttribute("lang");
25439         }
25440         
25441         if (node.hasAttribute("style")) {
25442             
25443             var styles = node.getAttribute("style").split(";");
25444             var nstyle = [];
25445             Roo.each(styles, function(s) {
25446                 if (!s.match(/:/)) {
25447                     return;
25448                 }
25449                 var kv = s.split(":");
25450                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25451                     return;
25452                 }
25453                 // what ever is left... we allow.
25454                 nstyle.push(s);
25455             });
25456             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25457             if (!nstyle.length) {
25458                 node.removeAttribute('style');
25459             }
25460         }
25461         this.iterateChildren(node, this.cleanWord);
25462         
25463         
25464         
25465     },
25466     /**
25467      * iterateChildren of a Node, calling fn each time, using this as the scole..
25468      * @param {DomNode} node node to iterate children of.
25469      * @param {Function} fn method of this class to call on each item.
25470      */
25471     iterateChildren : function(node, fn)
25472     {
25473         if (!node.childNodes.length) {
25474                 return;
25475         }
25476         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25477            fn.call(this, node.childNodes[i])
25478         }
25479     },
25480     
25481     
25482     /**
25483      * cleanTableWidths.
25484      *
25485      * Quite often pasting from word etc.. results in tables with column and widths.
25486      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25487      *
25488      */
25489     cleanTableWidths : function(node)
25490     {
25491          
25492          
25493         if (!node) {
25494             this.cleanTableWidths(this.doc.body);
25495             return;
25496         }
25497         
25498         // ignore list...
25499         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25500             return; 
25501         }
25502         Roo.log(node.tagName);
25503         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25504             this.iterateChildren(node, this.cleanTableWidths);
25505             return;
25506         }
25507         if (node.hasAttribute('width')) {
25508             node.removeAttribute('width');
25509         }
25510         
25511          
25512         if (node.hasAttribute("style")) {
25513             // pretty basic...
25514             
25515             var styles = node.getAttribute("style").split(";");
25516             var nstyle = [];
25517             Roo.each(styles, function(s) {
25518                 if (!s.match(/:/)) {
25519                     return;
25520                 }
25521                 var kv = s.split(":");
25522                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25523                     return;
25524                 }
25525                 // what ever is left... we allow.
25526                 nstyle.push(s);
25527             });
25528             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25529             if (!nstyle.length) {
25530                 node.removeAttribute('style');
25531             }
25532         }
25533         
25534         this.iterateChildren(node, this.cleanTableWidths);
25535         
25536         
25537     },
25538     
25539     
25540     
25541     
25542     domToHTML : function(currentElement, depth, nopadtext) {
25543         
25544         depth = depth || 0;
25545         nopadtext = nopadtext || false;
25546     
25547         if (!currentElement) {
25548             return this.domToHTML(this.doc.body);
25549         }
25550         
25551         //Roo.log(currentElement);
25552         var j;
25553         var allText = false;
25554         var nodeName = currentElement.nodeName;
25555         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25556         
25557         if  (nodeName == '#text') {
25558             
25559             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25560         }
25561         
25562         
25563         var ret = '';
25564         if (nodeName != 'BODY') {
25565              
25566             var i = 0;
25567             // Prints the node tagName, such as <A>, <IMG>, etc
25568             if (tagName) {
25569                 var attr = [];
25570                 for(i = 0; i < currentElement.attributes.length;i++) {
25571                     // quoting?
25572                     var aname = currentElement.attributes.item(i).name;
25573                     if (!currentElement.attributes.item(i).value.length) {
25574                         continue;
25575                     }
25576                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25577                 }
25578                 
25579                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25580             } 
25581             else {
25582                 
25583                 // eack
25584             }
25585         } else {
25586             tagName = false;
25587         }
25588         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25589             return ret;
25590         }
25591         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25592             nopadtext = true;
25593         }
25594         
25595         
25596         // Traverse the tree
25597         i = 0;
25598         var currentElementChild = currentElement.childNodes.item(i);
25599         var allText = true;
25600         var innerHTML  = '';
25601         lastnode = '';
25602         while (currentElementChild) {
25603             // Formatting code (indent the tree so it looks nice on the screen)
25604             var nopad = nopadtext;
25605             if (lastnode == 'SPAN') {
25606                 nopad  = true;
25607             }
25608             // text
25609             if  (currentElementChild.nodeName == '#text') {
25610                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25611                 toadd = nopadtext ? toadd : toadd.trim();
25612                 if (!nopad && toadd.length > 80) {
25613                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25614                 }
25615                 innerHTML  += toadd;
25616                 
25617                 i++;
25618                 currentElementChild = currentElement.childNodes.item(i);
25619                 lastNode = '';
25620                 continue;
25621             }
25622             allText = false;
25623             
25624             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25625                 
25626             // Recursively traverse the tree structure of the child node
25627             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25628             lastnode = currentElementChild.nodeName;
25629             i++;
25630             currentElementChild=currentElement.childNodes.item(i);
25631         }
25632         
25633         ret += innerHTML;
25634         
25635         if (!allText) {
25636                 // The remaining code is mostly for formatting the tree
25637             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25638         }
25639         
25640         
25641         if (tagName) {
25642             ret+= "</"+tagName+">";
25643         }
25644         return ret;
25645         
25646     },
25647         
25648     applyBlacklists : function()
25649     {
25650         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25651         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25652         
25653         this.white = [];
25654         this.black = [];
25655         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25656             if (b.indexOf(tag) > -1) {
25657                 return;
25658             }
25659             this.white.push(tag);
25660             
25661         }, this);
25662         
25663         Roo.each(w, function(tag) {
25664             if (b.indexOf(tag) > -1) {
25665                 return;
25666             }
25667             if (this.white.indexOf(tag) > -1) {
25668                 return;
25669             }
25670             this.white.push(tag);
25671             
25672         }, this);
25673         
25674         
25675         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25676             if (w.indexOf(tag) > -1) {
25677                 return;
25678             }
25679             this.black.push(tag);
25680             
25681         }, this);
25682         
25683         Roo.each(b, function(tag) {
25684             if (w.indexOf(tag) > -1) {
25685                 return;
25686             }
25687             if (this.black.indexOf(tag) > -1) {
25688                 return;
25689             }
25690             this.black.push(tag);
25691             
25692         }, this);
25693         
25694         
25695         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25696         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25697         
25698         this.cwhite = [];
25699         this.cblack = [];
25700         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25701             if (b.indexOf(tag) > -1) {
25702                 return;
25703             }
25704             this.cwhite.push(tag);
25705             
25706         }, this);
25707         
25708         Roo.each(w, function(tag) {
25709             if (b.indexOf(tag) > -1) {
25710                 return;
25711             }
25712             if (this.cwhite.indexOf(tag) > -1) {
25713                 return;
25714             }
25715             this.cwhite.push(tag);
25716             
25717         }, this);
25718         
25719         
25720         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25721             if (w.indexOf(tag) > -1) {
25722                 return;
25723             }
25724             this.cblack.push(tag);
25725             
25726         }, this);
25727         
25728         Roo.each(b, function(tag) {
25729             if (w.indexOf(tag) > -1) {
25730                 return;
25731             }
25732             if (this.cblack.indexOf(tag) > -1) {
25733                 return;
25734             }
25735             this.cblack.push(tag);
25736             
25737         }, this);
25738     },
25739     
25740     setStylesheets : function(stylesheets)
25741     {
25742         if(typeof(stylesheets) == 'string'){
25743             Roo.get(this.iframe.contentDocument.head).createChild({
25744                 tag : 'link',
25745                 rel : 'stylesheet',
25746                 type : 'text/css',
25747                 href : stylesheets
25748             });
25749             
25750             return;
25751         }
25752         var _this = this;
25753      
25754         Roo.each(stylesheets, function(s) {
25755             if(!s.length){
25756                 return;
25757             }
25758             
25759             Roo.get(_this.iframe.contentDocument.head).createChild({
25760                 tag : 'link',
25761                 rel : 'stylesheet',
25762                 type : 'text/css',
25763                 href : s
25764             });
25765         });
25766
25767         
25768     },
25769     
25770     removeStylesheets : function()
25771     {
25772         var _this = this;
25773         
25774         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25775             s.remove();
25776         });
25777     },
25778     
25779     setStyle : function(style)
25780     {
25781         Roo.get(this.iframe.contentDocument.head).createChild({
25782             tag : 'style',
25783             type : 'text/css',
25784             html : style
25785         });
25786
25787         return;
25788     }
25789     
25790     // hide stuff that is not compatible
25791     /**
25792      * @event blur
25793      * @hide
25794      */
25795     /**
25796      * @event change
25797      * @hide
25798      */
25799     /**
25800      * @event focus
25801      * @hide
25802      */
25803     /**
25804      * @event specialkey
25805      * @hide
25806      */
25807     /**
25808      * @cfg {String} fieldClass @hide
25809      */
25810     /**
25811      * @cfg {String} focusClass @hide
25812      */
25813     /**
25814      * @cfg {String} autoCreate @hide
25815      */
25816     /**
25817      * @cfg {String} inputType @hide
25818      */
25819     /**
25820      * @cfg {String} invalidClass @hide
25821      */
25822     /**
25823      * @cfg {String} invalidText @hide
25824      */
25825     /**
25826      * @cfg {String} msgFx @hide
25827      */
25828     /**
25829      * @cfg {String} validateOnBlur @hide
25830      */
25831 });
25832
25833 Roo.HtmlEditorCore.white = [
25834         'area', 'br', 'img', 'input', 'hr', 'wbr',
25835         
25836        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25837        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25838        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25839        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25840        'table',   'ul',         'xmp', 
25841        
25842        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25843       'thead',   'tr', 
25844      
25845       'dir', 'menu', 'ol', 'ul', 'dl',
25846        
25847       'embed',  'object'
25848 ];
25849
25850
25851 Roo.HtmlEditorCore.black = [
25852     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25853         'applet', // 
25854         'base',   'basefont', 'bgsound', 'blink',  'body', 
25855         'frame',  'frameset', 'head',    'html',   'ilayer', 
25856         'iframe', 'layer',  'link',     'meta',    'object',   
25857         'script', 'style' ,'title',  'xml' // clean later..
25858 ];
25859 Roo.HtmlEditorCore.clean = [
25860     'script', 'style', 'title', 'xml'
25861 ];
25862 Roo.HtmlEditorCore.remove = [
25863     'font'
25864 ];
25865 // attributes..
25866
25867 Roo.HtmlEditorCore.ablack = [
25868     'on'
25869 ];
25870     
25871 Roo.HtmlEditorCore.aclean = [ 
25872     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25873 ];
25874
25875 // protocols..
25876 Roo.HtmlEditorCore.pwhite= [
25877         'http',  'https',  'mailto'
25878 ];
25879
25880 // white listed style attributes.
25881 Roo.HtmlEditorCore.cwhite= [
25882       //  'text-align', /// default is to allow most things..
25883       
25884          
25885 //        'font-size'//??
25886 ];
25887
25888 // black listed style attributes.
25889 Roo.HtmlEditorCore.cblack= [
25890       //  'font-size' -- this can be set by the project 
25891 ];
25892
25893
25894 Roo.HtmlEditorCore.swapCodes   =[ 
25895     [    8211, "&#8211;" ], 
25896     [    8212, "&#8212;" ], 
25897     [    8216,  "'" ],  
25898     [    8217, "'" ],  
25899     [    8220, '"' ],  
25900     [    8221, '"' ],  
25901     [    8226, "*" ],  
25902     [    8230, "..." ]
25903 ]; 
25904
25905     /*
25906  * - LGPL
25907  *
25908  * HtmlEditor
25909  * 
25910  */
25911
25912 /**
25913  * @class Roo.bootstrap.HtmlEditor
25914  * @extends Roo.bootstrap.TextArea
25915  * Bootstrap HtmlEditor class
25916
25917  * @constructor
25918  * Create a new HtmlEditor
25919  * @param {Object} config The config object
25920  */
25921
25922 Roo.bootstrap.HtmlEditor = function(config){
25923     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25924     if (!this.toolbars) {
25925         this.toolbars = [];
25926     }
25927     
25928     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25929     this.addEvents({
25930             /**
25931              * @event initialize
25932              * Fires when the editor is fully initialized (including the iframe)
25933              * @param {HtmlEditor} this
25934              */
25935             initialize: true,
25936             /**
25937              * @event activate
25938              * Fires when the editor is first receives the focus. Any insertion must wait
25939              * until after this event.
25940              * @param {HtmlEditor} this
25941              */
25942             activate: true,
25943              /**
25944              * @event beforesync
25945              * Fires before the textarea is updated with content from the editor iframe. Return false
25946              * to cancel the sync.
25947              * @param {HtmlEditor} this
25948              * @param {String} html
25949              */
25950             beforesync: true,
25951              /**
25952              * @event beforepush
25953              * Fires before the iframe editor is updated with content from the textarea. Return false
25954              * to cancel the push.
25955              * @param {HtmlEditor} this
25956              * @param {String} html
25957              */
25958             beforepush: true,
25959              /**
25960              * @event sync
25961              * Fires when the textarea is updated with content from the editor iframe.
25962              * @param {HtmlEditor} this
25963              * @param {String} html
25964              */
25965             sync: true,
25966              /**
25967              * @event push
25968              * Fires when the iframe editor is updated with content from the textarea.
25969              * @param {HtmlEditor} this
25970              * @param {String} html
25971              */
25972             push: true,
25973              /**
25974              * @event editmodechange
25975              * Fires when the editor switches edit modes
25976              * @param {HtmlEditor} this
25977              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25978              */
25979             editmodechange: true,
25980             /**
25981              * @event editorevent
25982              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25983              * @param {HtmlEditor} this
25984              */
25985             editorevent: true,
25986             /**
25987              * @event firstfocus
25988              * Fires when on first focus - needed by toolbars..
25989              * @param {HtmlEditor} this
25990              */
25991             firstfocus: true,
25992             /**
25993              * @event autosave
25994              * Auto save the htmlEditor value as a file into Events
25995              * @param {HtmlEditor} this
25996              */
25997             autosave: true,
25998             /**
25999              * @event savedpreview
26000              * preview the saved version of htmlEditor
26001              * @param {HtmlEditor} this
26002              */
26003             savedpreview: true
26004         });
26005 };
26006
26007
26008 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26009     
26010     
26011       /**
26012      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26013      */
26014     toolbars : false,
26015     
26016      /**
26017     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26018     */
26019     btns : [],
26020    
26021      /**
26022      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26023      *                        Roo.resizable.
26024      */
26025     resizable : false,
26026      /**
26027      * @cfg {Number} height (in pixels)
26028      */   
26029     height: 300,
26030    /**
26031      * @cfg {Number} width (in pixels)
26032      */   
26033     width: false,
26034     
26035     /**
26036      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26037      * 
26038      */
26039     stylesheets: false,
26040     
26041     // id of frame..
26042     frameId: false,
26043     
26044     // private properties
26045     validationEvent : false,
26046     deferHeight: true,
26047     initialized : false,
26048     activated : false,
26049     
26050     onFocus : Roo.emptyFn,
26051     iframePad:3,
26052     hideMode:'offsets',
26053     
26054     tbContainer : false,
26055     
26056     bodyCls : '',
26057     
26058     toolbarContainer :function() {
26059         return this.wrap.select('.x-html-editor-tb',true).first();
26060     },
26061
26062     /**
26063      * Protected method that will not generally be called directly. It
26064      * is called when the editor creates its toolbar. Override this method if you need to
26065      * add custom toolbar buttons.
26066      * @param {HtmlEditor} editor
26067      */
26068     createToolbar : function(){
26069         Roo.log('renewing');
26070         Roo.log("create toolbars");
26071         
26072         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26073         this.toolbars[0].render(this.toolbarContainer());
26074         
26075         return;
26076         
26077 //        if (!editor.toolbars || !editor.toolbars.length) {
26078 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26079 //        }
26080 //        
26081 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26082 //            editor.toolbars[i] = Roo.factory(
26083 //                    typeof(editor.toolbars[i]) == 'string' ?
26084 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26085 //                Roo.bootstrap.HtmlEditor);
26086 //            editor.toolbars[i].init(editor);
26087 //        }
26088     },
26089
26090      
26091     // private
26092     onRender : function(ct, position)
26093     {
26094        // Roo.log("Call onRender: " + this.xtype);
26095         var _t = this;
26096         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26097       
26098         this.wrap = this.inputEl().wrap({
26099             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26100         });
26101         
26102         this.editorcore.onRender(ct, position);
26103          
26104         if (this.resizable) {
26105             this.resizeEl = new Roo.Resizable(this.wrap, {
26106                 pinned : true,
26107                 wrap: true,
26108                 dynamic : true,
26109                 minHeight : this.height,
26110                 height: this.height,
26111                 handles : this.resizable,
26112                 width: this.width,
26113                 listeners : {
26114                     resize : function(r, w, h) {
26115                         _t.onResize(w,h); // -something
26116                     }
26117                 }
26118             });
26119             
26120         }
26121         this.createToolbar(this);
26122        
26123         
26124         if(!this.width && this.resizable){
26125             this.setSize(this.wrap.getSize());
26126         }
26127         if (this.resizeEl) {
26128             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26129             // should trigger onReize..
26130         }
26131         
26132     },
26133
26134     // private
26135     onResize : function(w, h)
26136     {
26137         Roo.log('resize: ' +w + ',' + h );
26138         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26139         var ew = false;
26140         var eh = false;
26141         
26142         if(this.inputEl() ){
26143             if(typeof w == 'number'){
26144                 var aw = w - this.wrap.getFrameWidth('lr');
26145                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26146                 ew = aw;
26147             }
26148             if(typeof h == 'number'){
26149                  var tbh = -11;  // fixme it needs to tool bar size!
26150                 for (var i =0; i < this.toolbars.length;i++) {
26151                     // fixme - ask toolbars for heights?
26152                     tbh += this.toolbars[i].el.getHeight();
26153                     //if (this.toolbars[i].footer) {
26154                     //    tbh += this.toolbars[i].footer.el.getHeight();
26155                     //}
26156                 }
26157               
26158                 
26159                 
26160                 
26161                 
26162                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26163                 ah -= 5; // knock a few pixes off for look..
26164                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26165                 var eh = ah;
26166             }
26167         }
26168         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26169         this.editorcore.onResize(ew,eh);
26170         
26171     },
26172
26173     /**
26174      * Toggles the editor between standard and source edit mode.
26175      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26176      */
26177     toggleSourceEdit : function(sourceEditMode)
26178     {
26179         this.editorcore.toggleSourceEdit(sourceEditMode);
26180         
26181         if(this.editorcore.sourceEditMode){
26182             Roo.log('editor - showing textarea');
26183             
26184 //            Roo.log('in');
26185 //            Roo.log(this.syncValue());
26186             this.syncValue();
26187             this.inputEl().removeClass(['hide', 'x-hidden']);
26188             this.inputEl().dom.removeAttribute('tabIndex');
26189             this.inputEl().focus();
26190         }else{
26191             Roo.log('editor - hiding textarea');
26192 //            Roo.log('out')
26193 //            Roo.log(this.pushValue()); 
26194             this.pushValue();
26195             
26196             this.inputEl().addClass(['hide', 'x-hidden']);
26197             this.inputEl().dom.setAttribute('tabIndex', -1);
26198             //this.deferFocus();
26199         }
26200          
26201         if(this.resizable){
26202             this.setSize(this.wrap.getSize());
26203         }
26204         
26205         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26206     },
26207  
26208     // private (for BoxComponent)
26209     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26210
26211     // private (for BoxComponent)
26212     getResizeEl : function(){
26213         return this.wrap;
26214     },
26215
26216     // private (for BoxComponent)
26217     getPositionEl : function(){
26218         return this.wrap;
26219     },
26220
26221     // private
26222     initEvents : function(){
26223         this.originalValue = this.getValue();
26224     },
26225
26226 //    /**
26227 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26228 //     * @method
26229 //     */
26230 //    markInvalid : Roo.emptyFn,
26231 //    /**
26232 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26233 //     * @method
26234 //     */
26235 //    clearInvalid : Roo.emptyFn,
26236
26237     setValue : function(v){
26238         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26239         this.editorcore.pushValue();
26240     },
26241
26242      
26243     // private
26244     deferFocus : function(){
26245         this.focus.defer(10, this);
26246     },
26247
26248     // doc'ed in Field
26249     focus : function(){
26250         this.editorcore.focus();
26251         
26252     },
26253       
26254
26255     // private
26256     onDestroy : function(){
26257         
26258         
26259         
26260         if(this.rendered){
26261             
26262             for (var i =0; i < this.toolbars.length;i++) {
26263                 // fixme - ask toolbars for heights?
26264                 this.toolbars[i].onDestroy();
26265             }
26266             
26267             this.wrap.dom.innerHTML = '';
26268             this.wrap.remove();
26269         }
26270     },
26271
26272     // private
26273     onFirstFocus : function(){
26274         //Roo.log("onFirstFocus");
26275         this.editorcore.onFirstFocus();
26276          for (var i =0; i < this.toolbars.length;i++) {
26277             this.toolbars[i].onFirstFocus();
26278         }
26279         
26280     },
26281     
26282     // private
26283     syncValue : function()
26284     {   
26285         this.editorcore.syncValue();
26286     },
26287     
26288     pushValue : function()
26289     {   
26290         this.editorcore.pushValue();
26291     }
26292      
26293     
26294     // hide stuff that is not compatible
26295     /**
26296      * @event blur
26297      * @hide
26298      */
26299     /**
26300      * @event change
26301      * @hide
26302      */
26303     /**
26304      * @event focus
26305      * @hide
26306      */
26307     /**
26308      * @event specialkey
26309      * @hide
26310      */
26311     /**
26312      * @cfg {String} fieldClass @hide
26313      */
26314     /**
26315      * @cfg {String} focusClass @hide
26316      */
26317     /**
26318      * @cfg {String} autoCreate @hide
26319      */
26320     /**
26321      * @cfg {String} inputType @hide
26322      */
26323      
26324     /**
26325      * @cfg {String} invalidText @hide
26326      */
26327     /**
26328      * @cfg {String} msgFx @hide
26329      */
26330     /**
26331      * @cfg {String} validateOnBlur @hide
26332      */
26333 });
26334  
26335     
26336    
26337    
26338    
26339       
26340 Roo.namespace('Roo.bootstrap.htmleditor');
26341 /**
26342  * @class Roo.bootstrap.HtmlEditorToolbar1
26343  * Basic Toolbar
26344  * 
26345  * @example
26346  * Usage:
26347  *
26348  new Roo.bootstrap.HtmlEditor({
26349     ....
26350     toolbars : [
26351         new Roo.bootstrap.HtmlEditorToolbar1({
26352             disable : { fonts: 1 , format: 1, ..., ... , ...],
26353             btns : [ .... ]
26354         })
26355     }
26356      
26357  * 
26358  * @cfg {Object} disable List of elements to disable..
26359  * @cfg {Array} btns List of additional buttons.
26360  * 
26361  * 
26362  * NEEDS Extra CSS? 
26363  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26364  */
26365  
26366 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26367 {
26368     
26369     Roo.apply(this, config);
26370     
26371     // default disabled, based on 'good practice'..
26372     this.disable = this.disable || {};
26373     Roo.applyIf(this.disable, {
26374         fontSize : true,
26375         colors : true,
26376         specialElements : true
26377     });
26378     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26379     
26380     this.editor = config.editor;
26381     this.editorcore = config.editor.editorcore;
26382     
26383     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26384     
26385     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26386     // dont call parent... till later.
26387 }
26388 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26389      
26390     bar : true,
26391     
26392     editor : false,
26393     editorcore : false,
26394     
26395     
26396     formats : [
26397         "p" ,  
26398         "h1","h2","h3","h4","h5","h6", 
26399         "pre", "code", 
26400         "abbr", "acronym", "address", "cite", "samp", "var",
26401         'div','span'
26402     ],
26403     
26404     onRender : function(ct, position)
26405     {
26406        // Roo.log("Call onRender: " + this.xtype);
26407         
26408        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26409        Roo.log(this.el);
26410        this.el.dom.style.marginBottom = '0';
26411        var _this = this;
26412        var editorcore = this.editorcore;
26413        var editor= this.editor;
26414        
26415        var children = [];
26416        var btn = function(id,cmd , toggle, handler, html){
26417        
26418             var  event = toggle ? 'toggle' : 'click';
26419        
26420             var a = {
26421                 size : 'sm',
26422                 xtype: 'Button',
26423                 xns: Roo.bootstrap,
26424                 //glyphicon : id,
26425                 fa: id,
26426                 cmd : id || cmd,
26427                 enableToggle:toggle !== false,
26428                 html : html || '',
26429                 pressed : toggle ? false : null,
26430                 listeners : {}
26431             };
26432             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26433                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26434             };
26435             children.push(a);
26436             return a;
26437        }
26438        
26439     //    var cb_box = function...
26440         
26441         var style = {
26442                 xtype: 'Button',
26443                 size : 'sm',
26444                 xns: Roo.bootstrap,
26445                 fa : 'font',
26446                 //html : 'submit'
26447                 menu : {
26448                     xtype: 'Menu',
26449                     xns: Roo.bootstrap,
26450                     items:  []
26451                 }
26452         };
26453         Roo.each(this.formats, function(f) {
26454             style.menu.items.push({
26455                 xtype :'MenuItem',
26456                 xns: Roo.bootstrap,
26457                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26458                 tagname : f,
26459                 listeners : {
26460                     click : function()
26461                     {
26462                         editorcore.insertTag(this.tagname);
26463                         editor.focus();
26464                     }
26465                 }
26466                 
26467             });
26468         });
26469         children.push(style);   
26470         
26471         btn('bold',false,true);
26472         btn('italic',false,true);
26473         btn('align-left', 'justifyleft',true);
26474         btn('align-center', 'justifycenter',true);
26475         btn('align-right' , 'justifyright',true);
26476         btn('link', false, false, function(btn) {
26477             //Roo.log("create link?");
26478             var url = prompt(this.createLinkText, this.defaultLinkValue);
26479             if(url && url != 'http:/'+'/'){
26480                 this.editorcore.relayCmd('createlink', url);
26481             }
26482         }),
26483         btn('list','insertunorderedlist',true);
26484         btn('pencil', false,true, function(btn){
26485                 Roo.log(this);
26486                 this.toggleSourceEdit(btn.pressed);
26487         });
26488         
26489         if (this.editor.btns.length > 0) {
26490             for (var i = 0; i<this.editor.btns.length; i++) {
26491                 children.push(this.editor.btns[i]);
26492             }
26493         }
26494         
26495         /*
26496         var cog = {
26497                 xtype: 'Button',
26498                 size : 'sm',
26499                 xns: Roo.bootstrap,
26500                 glyphicon : 'cog',
26501                 //html : 'submit'
26502                 menu : {
26503                     xtype: 'Menu',
26504                     xns: Roo.bootstrap,
26505                     items:  []
26506                 }
26507         };
26508         
26509         cog.menu.items.push({
26510             xtype :'MenuItem',
26511             xns: Roo.bootstrap,
26512             html : Clean styles,
26513             tagname : f,
26514             listeners : {
26515                 click : function()
26516                 {
26517                     editorcore.insertTag(this.tagname);
26518                     editor.focus();
26519                 }
26520             }
26521             
26522         });
26523        */
26524         
26525          
26526        this.xtype = 'NavSimplebar';
26527         
26528         for(var i=0;i< children.length;i++) {
26529             
26530             this.buttons.add(this.addxtypeChild(children[i]));
26531             
26532         }
26533         
26534         editor.on('editorevent', this.updateToolbar, this);
26535     },
26536     onBtnClick : function(id)
26537     {
26538        this.editorcore.relayCmd(id);
26539        this.editorcore.focus();
26540     },
26541     
26542     /**
26543      * Protected method that will not generally be called directly. It triggers
26544      * a toolbar update by reading the markup state of the current selection in the editor.
26545      */
26546     updateToolbar: function(){
26547
26548         if(!this.editorcore.activated){
26549             this.editor.onFirstFocus(); // is this neeed?
26550             return;
26551         }
26552
26553         var btns = this.buttons; 
26554         var doc = this.editorcore.doc;
26555         btns.get('bold').setActive(doc.queryCommandState('bold'));
26556         btns.get('italic').setActive(doc.queryCommandState('italic'));
26557         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26558         
26559         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26560         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26561         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26562         
26563         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26564         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26565          /*
26566         
26567         var ans = this.editorcore.getAllAncestors();
26568         if (this.formatCombo) {
26569             
26570             
26571             var store = this.formatCombo.store;
26572             this.formatCombo.setValue("");
26573             for (var i =0; i < ans.length;i++) {
26574                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26575                     // select it..
26576                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26577                     break;
26578                 }
26579             }
26580         }
26581         
26582         
26583         
26584         // hides menus... - so this cant be on a menu...
26585         Roo.bootstrap.MenuMgr.hideAll();
26586         */
26587         Roo.bootstrap.MenuMgr.hideAll();
26588         //this.editorsyncValue();
26589     },
26590     onFirstFocus: function() {
26591         this.buttons.each(function(item){
26592            item.enable();
26593         });
26594     },
26595     toggleSourceEdit : function(sourceEditMode){
26596         
26597           
26598         if(sourceEditMode){
26599             Roo.log("disabling buttons");
26600            this.buttons.each( function(item){
26601                 if(item.cmd != 'pencil'){
26602                     item.disable();
26603                 }
26604             });
26605           
26606         }else{
26607             Roo.log("enabling buttons");
26608             if(this.editorcore.initialized){
26609                 this.buttons.each( function(item){
26610                     item.enable();
26611                 });
26612             }
26613             
26614         }
26615         Roo.log("calling toggole on editor");
26616         // tell the editor that it's been pressed..
26617         this.editor.toggleSourceEdit(sourceEditMode);
26618        
26619     }
26620 });
26621
26622
26623
26624
26625  
26626 /*
26627  * - LGPL
26628  */
26629
26630 /**
26631  * @class Roo.bootstrap.Markdown
26632  * @extends Roo.bootstrap.TextArea
26633  * Bootstrap Showdown editable area
26634  * @cfg {string} content
26635  * 
26636  * @constructor
26637  * Create a new Showdown
26638  */
26639
26640 Roo.bootstrap.Markdown = function(config){
26641     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26642    
26643 };
26644
26645 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26646     
26647     editing :false,
26648     
26649     initEvents : function()
26650     {
26651         
26652         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26653         this.markdownEl = this.el.createChild({
26654             cls : 'roo-markdown-area'
26655         });
26656         this.inputEl().addClass('d-none');
26657         if (this.getValue() == '') {
26658             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26659             
26660         } else {
26661             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26662         }
26663         this.markdownEl.on('click', this.toggleTextEdit, this);
26664         this.on('blur', this.toggleTextEdit, this);
26665         this.on('specialkey', this.resizeTextArea, this);
26666     },
26667     
26668     toggleTextEdit : function()
26669     {
26670         var sh = this.markdownEl.getHeight();
26671         this.inputEl().addClass('d-none');
26672         this.markdownEl.addClass('d-none');
26673         if (!this.editing) {
26674             // show editor?
26675             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26676             this.inputEl().removeClass('d-none');
26677             this.inputEl().focus();
26678             this.editing = true;
26679             return;
26680         }
26681         // show showdown...
26682         this.updateMarkdown();
26683         this.markdownEl.removeClass('d-none');
26684         this.editing = false;
26685         return;
26686     },
26687     updateMarkdown : function()
26688     {
26689         if (this.getValue() == '') {
26690             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26691             return;
26692         }
26693  
26694         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26695     },
26696     
26697     resizeTextArea: function () {
26698         
26699         var sh = 100;
26700         Roo.log([sh, this.getValue().split("\n").length * 30]);
26701         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26702     },
26703     setValue : function(val)
26704     {
26705         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26706         if (!this.editing) {
26707             this.updateMarkdown();
26708         }
26709         
26710     },
26711     focus : function()
26712     {
26713         if (!this.editing) {
26714             this.toggleTextEdit();
26715         }
26716         
26717     }
26718
26719
26720 });
26721 /**
26722  * @class Roo.bootstrap.Table.AbstractSelectionModel
26723  * @extends Roo.util.Observable
26724  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26725  * implemented by descendant classes.  This class should not be directly instantiated.
26726  * @constructor
26727  */
26728 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26729     this.locked = false;
26730     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26731 };
26732
26733
26734 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26735     /** @ignore Called by the grid automatically. Do not call directly. */
26736     init : function(grid){
26737         this.grid = grid;
26738         this.initEvents();
26739     },
26740
26741     /**
26742      * Locks the selections.
26743      */
26744     lock : function(){
26745         this.locked = true;
26746     },
26747
26748     /**
26749      * Unlocks the selections.
26750      */
26751     unlock : function(){
26752         this.locked = false;
26753     },
26754
26755     /**
26756      * Returns true if the selections are locked.
26757      * @return {Boolean}
26758      */
26759     isLocked : function(){
26760         return this.locked;
26761     },
26762     
26763     
26764     initEvents : function ()
26765     {
26766         
26767     }
26768 });
26769 /**
26770  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26771  * @class Roo.bootstrap.Table.RowSelectionModel
26772  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26773  * It supports multiple selections and keyboard selection/navigation. 
26774  * @constructor
26775  * @param {Object} config
26776  */
26777
26778 Roo.bootstrap.Table.RowSelectionModel = function(config){
26779     Roo.apply(this, config);
26780     this.selections = new Roo.util.MixedCollection(false, function(o){
26781         return o.id;
26782     });
26783
26784     this.last = false;
26785     this.lastActive = false;
26786
26787     this.addEvents({
26788         /**
26789              * @event selectionchange
26790              * Fires when the selection changes
26791              * @param {SelectionModel} this
26792              */
26793             "selectionchange" : true,
26794         /**
26795              * @event afterselectionchange
26796              * Fires after the selection changes (eg. by key press or clicking)
26797              * @param {SelectionModel} this
26798              */
26799             "afterselectionchange" : true,
26800         /**
26801              * @event beforerowselect
26802              * Fires when a row is selected being selected, return false to cancel.
26803              * @param {SelectionModel} this
26804              * @param {Number} rowIndex The selected index
26805              * @param {Boolean} keepExisting False if other selections will be cleared
26806              */
26807             "beforerowselect" : true,
26808         /**
26809              * @event rowselect
26810              * Fires when a row is selected.
26811              * @param {SelectionModel} this
26812              * @param {Number} rowIndex The selected index
26813              * @param {Roo.data.Record} r The record
26814              */
26815             "rowselect" : true,
26816         /**
26817              * @event rowdeselect
26818              * Fires when a row is deselected.
26819              * @param {SelectionModel} this
26820              * @param {Number} rowIndex The selected index
26821              */
26822         "rowdeselect" : true
26823     });
26824     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26825     this.locked = false;
26826  };
26827
26828 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26829     /**
26830      * @cfg {Boolean} singleSelect
26831      * True to allow selection of only one row at a time (defaults to false)
26832      */
26833     singleSelect : false,
26834
26835     // private
26836     initEvents : function()
26837     {
26838
26839         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26840         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26841         //}else{ // allow click to work like normal
26842          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26843         //}
26844         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26845         this.grid.on("rowclick", this.handleMouseDown, this);
26846         
26847         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26848             "up" : function(e){
26849                 if(!e.shiftKey){
26850                     this.selectPrevious(e.shiftKey);
26851                 }else if(this.last !== false && this.lastActive !== false){
26852                     var last = this.last;
26853                     this.selectRange(this.last,  this.lastActive-1);
26854                     this.grid.getView().focusRow(this.lastActive);
26855                     if(last !== false){
26856                         this.last = last;
26857                     }
26858                 }else{
26859                     this.selectFirstRow();
26860                 }
26861                 this.fireEvent("afterselectionchange", this);
26862             },
26863             "down" : function(e){
26864                 if(!e.shiftKey){
26865                     this.selectNext(e.shiftKey);
26866                 }else if(this.last !== false && this.lastActive !== false){
26867                     var last = this.last;
26868                     this.selectRange(this.last,  this.lastActive+1);
26869                     this.grid.getView().focusRow(this.lastActive);
26870                     if(last !== false){
26871                         this.last = last;
26872                     }
26873                 }else{
26874                     this.selectFirstRow();
26875                 }
26876                 this.fireEvent("afterselectionchange", this);
26877             },
26878             scope: this
26879         });
26880         this.grid.store.on('load', function(){
26881             this.selections.clear();
26882         },this);
26883         /*
26884         var view = this.grid.view;
26885         view.on("refresh", this.onRefresh, this);
26886         view.on("rowupdated", this.onRowUpdated, this);
26887         view.on("rowremoved", this.onRemove, this);
26888         */
26889     },
26890
26891     // private
26892     onRefresh : function()
26893     {
26894         var ds = this.grid.store, i, v = this.grid.view;
26895         var s = this.selections;
26896         s.each(function(r){
26897             if((i = ds.indexOfId(r.id)) != -1){
26898                 v.onRowSelect(i);
26899             }else{
26900                 s.remove(r);
26901             }
26902         });
26903     },
26904
26905     // private
26906     onRemove : function(v, index, r){
26907         this.selections.remove(r);
26908     },
26909
26910     // private
26911     onRowUpdated : function(v, index, r){
26912         if(this.isSelected(r)){
26913             v.onRowSelect(index);
26914         }
26915     },
26916
26917     /**
26918      * Select records.
26919      * @param {Array} records The records to select
26920      * @param {Boolean} keepExisting (optional) True to keep existing selections
26921      */
26922     selectRecords : function(records, keepExisting)
26923     {
26924         if(!keepExisting){
26925             this.clearSelections();
26926         }
26927             var ds = this.grid.store;
26928         for(var i = 0, len = records.length; i < len; i++){
26929             this.selectRow(ds.indexOf(records[i]), true);
26930         }
26931     },
26932
26933     /**
26934      * Gets the number of selected rows.
26935      * @return {Number}
26936      */
26937     getCount : function(){
26938         return this.selections.length;
26939     },
26940
26941     /**
26942      * Selects the first row in the grid.
26943      */
26944     selectFirstRow : function(){
26945         this.selectRow(0);
26946     },
26947
26948     /**
26949      * Select the last row.
26950      * @param {Boolean} keepExisting (optional) True to keep existing selections
26951      */
26952     selectLastRow : function(keepExisting){
26953         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26954         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26955     },
26956
26957     /**
26958      * Selects the row immediately following the last selected row.
26959      * @param {Boolean} keepExisting (optional) True to keep existing selections
26960      */
26961     selectNext : function(keepExisting)
26962     {
26963             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26964             this.selectRow(this.last+1, keepExisting);
26965             this.grid.getView().focusRow(this.last);
26966         }
26967     },
26968
26969     /**
26970      * Selects the row that precedes the last selected row.
26971      * @param {Boolean} keepExisting (optional) True to keep existing selections
26972      */
26973     selectPrevious : function(keepExisting){
26974         if(this.last){
26975             this.selectRow(this.last-1, keepExisting);
26976             this.grid.getView().focusRow(this.last);
26977         }
26978     },
26979
26980     /**
26981      * Returns the selected records
26982      * @return {Array} Array of selected records
26983      */
26984     getSelections : function(){
26985         return [].concat(this.selections.items);
26986     },
26987
26988     /**
26989      * Returns the first selected record.
26990      * @return {Record}
26991      */
26992     getSelected : function(){
26993         return this.selections.itemAt(0);
26994     },
26995
26996
26997     /**
26998      * Clears all selections.
26999      */
27000     clearSelections : function(fast)
27001     {
27002         if(this.locked) {
27003             return;
27004         }
27005         if(fast !== true){
27006                 var ds = this.grid.store;
27007             var s = this.selections;
27008             s.each(function(r){
27009                 this.deselectRow(ds.indexOfId(r.id));
27010             }, this);
27011             s.clear();
27012         }else{
27013             this.selections.clear();
27014         }
27015         this.last = false;
27016     },
27017
27018
27019     /**
27020      * Selects all rows.
27021      */
27022     selectAll : function(){
27023         if(this.locked) {
27024             return;
27025         }
27026         this.selections.clear();
27027         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27028             this.selectRow(i, true);
27029         }
27030     },
27031
27032     /**
27033      * Returns True if there is a selection.
27034      * @return {Boolean}
27035      */
27036     hasSelection : function(){
27037         return this.selections.length > 0;
27038     },
27039
27040     /**
27041      * Returns True if the specified row is selected.
27042      * @param {Number/Record} record The record or index of the record to check
27043      * @return {Boolean}
27044      */
27045     isSelected : function(index){
27046             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27047         return (r && this.selections.key(r.id) ? true : false);
27048     },
27049
27050     /**
27051      * Returns True if the specified record id is selected.
27052      * @param {String} id The id of record to check
27053      * @return {Boolean}
27054      */
27055     isIdSelected : function(id){
27056         return (this.selections.key(id) ? true : false);
27057     },
27058
27059
27060     // private
27061     handleMouseDBClick : function(e, t){
27062         
27063     },
27064     // private
27065     handleMouseDown : function(e, t)
27066     {
27067             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27068         if(this.isLocked() || rowIndex < 0 ){
27069             return;
27070         };
27071         if(e.shiftKey && this.last !== false){
27072             var last = this.last;
27073             this.selectRange(last, rowIndex, e.ctrlKey);
27074             this.last = last; // reset the last
27075             t.focus();
27076     
27077         }else{
27078             var isSelected = this.isSelected(rowIndex);
27079             //Roo.log("select row:" + rowIndex);
27080             if(isSelected){
27081                 this.deselectRow(rowIndex);
27082             } else {
27083                         this.selectRow(rowIndex, true);
27084             }
27085     
27086             /*
27087                 if(e.button !== 0 && isSelected){
27088                 alert('rowIndex 2: ' + rowIndex);
27089                     view.focusRow(rowIndex);
27090                 }else if(e.ctrlKey && isSelected){
27091                     this.deselectRow(rowIndex);
27092                 }else if(!isSelected){
27093                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27094                     view.focusRow(rowIndex);
27095                 }
27096             */
27097         }
27098         this.fireEvent("afterselectionchange", this);
27099     },
27100     // private
27101     handleDragableRowClick :  function(grid, rowIndex, e) 
27102     {
27103         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27104             this.selectRow(rowIndex, false);
27105             grid.view.focusRow(rowIndex);
27106              this.fireEvent("afterselectionchange", this);
27107         }
27108     },
27109     
27110     /**
27111      * Selects multiple rows.
27112      * @param {Array} rows Array of the indexes of the row to select
27113      * @param {Boolean} keepExisting (optional) True to keep existing selections
27114      */
27115     selectRows : function(rows, keepExisting){
27116         if(!keepExisting){
27117             this.clearSelections();
27118         }
27119         for(var i = 0, len = rows.length; i < len; i++){
27120             this.selectRow(rows[i], true);
27121         }
27122     },
27123
27124     /**
27125      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27126      * @param {Number} startRow The index of the first row in the range
27127      * @param {Number} endRow The index of the last row in the range
27128      * @param {Boolean} keepExisting (optional) True to retain existing selections
27129      */
27130     selectRange : function(startRow, endRow, keepExisting){
27131         if(this.locked) {
27132             return;
27133         }
27134         if(!keepExisting){
27135             this.clearSelections();
27136         }
27137         if(startRow <= endRow){
27138             for(var i = startRow; i <= endRow; i++){
27139                 this.selectRow(i, true);
27140             }
27141         }else{
27142             for(var i = startRow; i >= endRow; i--){
27143                 this.selectRow(i, true);
27144             }
27145         }
27146     },
27147
27148     /**
27149      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27150      * @param {Number} startRow The index of the first row in the range
27151      * @param {Number} endRow The index of the last row in the range
27152      */
27153     deselectRange : function(startRow, endRow, preventViewNotify){
27154         if(this.locked) {
27155             return;
27156         }
27157         for(var i = startRow; i <= endRow; i++){
27158             this.deselectRow(i, preventViewNotify);
27159         }
27160     },
27161
27162     /**
27163      * Selects a row.
27164      * @param {Number} row The index of the row to select
27165      * @param {Boolean} keepExisting (optional) True to keep existing selections
27166      */
27167     selectRow : function(index, keepExisting, preventViewNotify)
27168     {
27169             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27170             return;
27171         }
27172         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27173             if(!keepExisting || this.singleSelect){
27174                 this.clearSelections();
27175             }
27176             
27177             var r = this.grid.store.getAt(index);
27178             //console.log('selectRow - record id :' + r.id);
27179             
27180             this.selections.add(r);
27181             this.last = this.lastActive = index;
27182             if(!preventViewNotify){
27183                 var proxy = new Roo.Element(
27184                                 this.grid.getRowDom(index)
27185                 );
27186                 proxy.addClass('bg-info info');
27187             }
27188             this.fireEvent("rowselect", this, index, r);
27189             this.fireEvent("selectionchange", this);
27190         }
27191     },
27192
27193     /**
27194      * Deselects a row.
27195      * @param {Number} row The index of the row to deselect
27196      */
27197     deselectRow : function(index, preventViewNotify)
27198     {
27199         if(this.locked) {
27200             return;
27201         }
27202         if(this.last == index){
27203             this.last = false;
27204         }
27205         if(this.lastActive == index){
27206             this.lastActive = false;
27207         }
27208         
27209         var r = this.grid.store.getAt(index);
27210         if (!r) {
27211             return;
27212         }
27213         
27214         this.selections.remove(r);
27215         //.console.log('deselectRow - record id :' + r.id);
27216         if(!preventViewNotify){
27217         
27218             var proxy = new Roo.Element(
27219                 this.grid.getRowDom(index)
27220             );
27221             proxy.removeClass('bg-info info');
27222         }
27223         this.fireEvent("rowdeselect", this, index);
27224         this.fireEvent("selectionchange", this);
27225     },
27226
27227     // private
27228     restoreLast : function(){
27229         if(this._last){
27230             this.last = this._last;
27231         }
27232     },
27233
27234     // private
27235     acceptsNav : function(row, col, cm){
27236         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27237     },
27238
27239     // private
27240     onEditorKey : function(field, e){
27241         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27242         if(k == e.TAB){
27243             e.stopEvent();
27244             ed.completeEdit();
27245             if(e.shiftKey){
27246                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27247             }else{
27248                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27249             }
27250         }else if(k == e.ENTER && !e.ctrlKey){
27251             e.stopEvent();
27252             ed.completeEdit();
27253             if(e.shiftKey){
27254                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27255             }else{
27256                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27257             }
27258         }else if(k == e.ESC){
27259             ed.cancelEdit();
27260         }
27261         if(newCell){
27262             g.startEditing(newCell[0], newCell[1]);
27263         }
27264     }
27265 });
27266 /*
27267  * Based on:
27268  * Ext JS Library 1.1.1
27269  * Copyright(c) 2006-2007, Ext JS, LLC.
27270  *
27271  * Originally Released Under LGPL - original licence link has changed is not relivant.
27272  *
27273  * Fork - LGPL
27274  * <script type="text/javascript">
27275  */
27276  
27277 /**
27278  * @class Roo.bootstrap.PagingToolbar
27279  * @extends Roo.bootstrap.NavSimplebar
27280  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27281  * @constructor
27282  * Create a new PagingToolbar
27283  * @param {Object} config The config object
27284  * @param {Roo.data.Store} store
27285  */
27286 Roo.bootstrap.PagingToolbar = function(config)
27287 {
27288     // old args format still supported... - xtype is prefered..
27289         // created from xtype...
27290     
27291     this.ds = config.dataSource;
27292     
27293     if (config.store && !this.ds) {
27294         this.store= Roo.factory(config.store, Roo.data);
27295         this.ds = this.store;
27296         this.ds.xmodule = this.xmodule || false;
27297     }
27298     
27299     this.toolbarItems = [];
27300     if (config.items) {
27301         this.toolbarItems = config.items;
27302     }
27303     
27304     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27305     
27306     this.cursor = 0;
27307     
27308     if (this.ds) { 
27309         this.bind(this.ds);
27310     }
27311     
27312     if (Roo.bootstrap.version == 4) {
27313         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27314     } else {
27315         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27316     }
27317     
27318 };
27319
27320 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27321     /**
27322      * @cfg {Roo.data.Store} dataSource
27323      * The underlying data store providing the paged data
27324      */
27325     /**
27326      * @cfg {String/HTMLElement/Element} container
27327      * container The id or element that will contain the toolbar
27328      */
27329     /**
27330      * @cfg {Boolean} displayInfo
27331      * True to display the displayMsg (defaults to false)
27332      */
27333     /**
27334      * @cfg {Number} pageSize
27335      * The number of records to display per page (defaults to 20)
27336      */
27337     pageSize: 20,
27338     /**
27339      * @cfg {String} displayMsg
27340      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27341      */
27342     displayMsg : 'Displaying {0} - {1} of {2}',
27343     /**
27344      * @cfg {String} emptyMsg
27345      * The message to display when no records are found (defaults to "No data to display")
27346      */
27347     emptyMsg : 'No data to display',
27348     /**
27349      * Customizable piece of the default paging text (defaults to "Page")
27350      * @type String
27351      */
27352     beforePageText : "Page",
27353     /**
27354      * Customizable piece of the default paging text (defaults to "of %0")
27355      * @type String
27356      */
27357     afterPageText : "of {0}",
27358     /**
27359      * Customizable piece of the default paging text (defaults to "First Page")
27360      * @type String
27361      */
27362     firstText : "First Page",
27363     /**
27364      * Customizable piece of the default paging text (defaults to "Previous Page")
27365      * @type String
27366      */
27367     prevText : "Previous Page",
27368     /**
27369      * Customizable piece of the default paging text (defaults to "Next Page")
27370      * @type String
27371      */
27372     nextText : "Next Page",
27373     /**
27374      * Customizable piece of the default paging text (defaults to "Last Page")
27375      * @type String
27376      */
27377     lastText : "Last Page",
27378     /**
27379      * Customizable piece of the default paging text (defaults to "Refresh")
27380      * @type String
27381      */
27382     refreshText : "Refresh",
27383
27384     buttons : false,
27385     // private
27386     onRender : function(ct, position) 
27387     {
27388         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27389         this.navgroup.parentId = this.id;
27390         this.navgroup.onRender(this.el, null);
27391         // add the buttons to the navgroup
27392         
27393         if(this.displayInfo){
27394             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27395             this.displayEl = this.el.select('.x-paging-info', true).first();
27396 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27397 //            this.displayEl = navel.el.select('span',true).first();
27398         }
27399         
27400         var _this = this;
27401         
27402         if(this.buttons){
27403             Roo.each(_this.buttons, function(e){ // this might need to use render????
27404                Roo.factory(e).render(_this.el);
27405             });
27406         }
27407             
27408         Roo.each(_this.toolbarItems, function(e) {
27409             _this.navgroup.addItem(e);
27410         });
27411         
27412         
27413         this.first = this.navgroup.addItem({
27414             tooltip: this.firstText,
27415             cls: "prev btn-outline-secondary",
27416             html : ' <i class="fa fa-step-backward"></i>',
27417             disabled: true,
27418             preventDefault: true,
27419             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27420         });
27421         
27422         this.prev =  this.navgroup.addItem({
27423             tooltip: this.prevText,
27424             cls: "prev btn-outline-secondary",
27425             html : ' <i class="fa fa-backward"></i>',
27426             disabled: true,
27427             preventDefault: true,
27428             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27429         });
27430     //this.addSeparator();
27431         
27432         
27433         var field = this.navgroup.addItem( {
27434             tagtype : 'span',
27435             cls : 'x-paging-position  btn-outline-secondary',
27436              disabled: true,
27437             html : this.beforePageText  +
27438                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27439                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27440          } ); //?? escaped?
27441         
27442         this.field = field.el.select('input', true).first();
27443         this.field.on("keydown", this.onPagingKeydown, this);
27444         this.field.on("focus", function(){this.dom.select();});
27445     
27446     
27447         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27448         //this.field.setHeight(18);
27449         //this.addSeparator();
27450         this.next = this.navgroup.addItem({
27451             tooltip: this.nextText,
27452             cls: "next btn-outline-secondary",
27453             html : ' <i class="fa fa-forward"></i>',
27454             disabled: true,
27455             preventDefault: true,
27456             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27457         });
27458         this.last = this.navgroup.addItem({
27459             tooltip: this.lastText,
27460             html : ' <i class="fa fa-step-forward"></i>',
27461             cls: "next btn-outline-secondary",
27462             disabled: true,
27463             preventDefault: true,
27464             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27465         });
27466     //this.addSeparator();
27467         this.loading = this.navgroup.addItem({
27468             tooltip: this.refreshText,
27469             cls: "btn-outline-secondary",
27470             html : ' <i class="fa fa-refresh"></i>',
27471             preventDefault: true,
27472             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27473         });
27474         
27475     },
27476
27477     // private
27478     updateInfo : function(){
27479         if(this.displayEl){
27480             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27481             var msg = count == 0 ?
27482                 this.emptyMsg :
27483                 String.format(
27484                     this.displayMsg,
27485                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27486                 );
27487             this.displayEl.update(msg);
27488         }
27489     },
27490
27491     // private
27492     onLoad : function(ds, r, o)
27493     {
27494         this.cursor = o.params && o.params.start ? o.params.start : 0;
27495         
27496         var d = this.getPageData(),
27497             ap = d.activePage,
27498             ps = d.pages;
27499         
27500         
27501         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27502         this.field.dom.value = ap;
27503         this.first.setDisabled(ap == 1);
27504         this.prev.setDisabled(ap == 1);
27505         this.next.setDisabled(ap == ps);
27506         this.last.setDisabled(ap == ps);
27507         this.loading.enable();
27508         this.updateInfo();
27509     },
27510
27511     // private
27512     getPageData : function(){
27513         var total = this.ds.getTotalCount();
27514         return {
27515             total : total,
27516             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27517             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27518         };
27519     },
27520
27521     // private
27522     onLoadError : function(){
27523         this.loading.enable();
27524     },
27525
27526     // private
27527     onPagingKeydown : function(e){
27528         var k = e.getKey();
27529         var d = this.getPageData();
27530         if(k == e.RETURN){
27531             var v = this.field.dom.value, pageNum;
27532             if(!v || isNaN(pageNum = parseInt(v, 10))){
27533                 this.field.dom.value = d.activePage;
27534                 return;
27535             }
27536             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27537             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27538             e.stopEvent();
27539         }
27540         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))
27541         {
27542           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27543           this.field.dom.value = pageNum;
27544           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27545           e.stopEvent();
27546         }
27547         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27548         {
27549           var v = this.field.dom.value, pageNum; 
27550           var increment = (e.shiftKey) ? 10 : 1;
27551           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27552                 increment *= -1;
27553           }
27554           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27555             this.field.dom.value = d.activePage;
27556             return;
27557           }
27558           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27559           {
27560             this.field.dom.value = parseInt(v, 10) + increment;
27561             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27562             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27563           }
27564           e.stopEvent();
27565         }
27566     },
27567
27568     // private
27569     beforeLoad : function(){
27570         if(this.loading){
27571             this.loading.disable();
27572         }
27573     },
27574
27575     // private
27576     onClick : function(which){
27577         
27578         var ds = this.ds;
27579         if (!ds) {
27580             return;
27581         }
27582         
27583         switch(which){
27584             case "first":
27585                 ds.load({params:{start: 0, limit: this.pageSize}});
27586             break;
27587             case "prev":
27588                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27589             break;
27590             case "next":
27591                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27592             break;
27593             case "last":
27594                 var total = ds.getTotalCount();
27595                 var extra = total % this.pageSize;
27596                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27597                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27598             break;
27599             case "refresh":
27600                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27601             break;
27602         }
27603     },
27604
27605     /**
27606      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27607      * @param {Roo.data.Store} store The data store to unbind
27608      */
27609     unbind : function(ds){
27610         ds.un("beforeload", this.beforeLoad, this);
27611         ds.un("load", this.onLoad, this);
27612         ds.un("loadexception", this.onLoadError, this);
27613         ds.un("remove", this.updateInfo, this);
27614         ds.un("add", this.updateInfo, this);
27615         this.ds = undefined;
27616     },
27617
27618     /**
27619      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27620      * @param {Roo.data.Store} store The data store to bind
27621      */
27622     bind : function(ds){
27623         ds.on("beforeload", this.beforeLoad, this);
27624         ds.on("load", this.onLoad, this);
27625         ds.on("loadexception", this.onLoadError, this);
27626         ds.on("remove", this.updateInfo, this);
27627         ds.on("add", this.updateInfo, this);
27628         this.ds = ds;
27629     }
27630 });/*
27631  * - LGPL
27632  *
27633  * element
27634  * 
27635  */
27636
27637 /**
27638  * @class Roo.bootstrap.MessageBar
27639  * @extends Roo.bootstrap.Component
27640  * Bootstrap MessageBar class
27641  * @cfg {String} html contents of the MessageBar
27642  * @cfg {String} weight (info | success | warning | danger) default info
27643  * @cfg {String} beforeClass insert the bar before the given class
27644  * @cfg {Boolean} closable (true | false) default false
27645  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27646  * 
27647  * @constructor
27648  * Create a new Element
27649  * @param {Object} config The config object
27650  */
27651
27652 Roo.bootstrap.MessageBar = function(config){
27653     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27654 };
27655
27656 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27657     
27658     html: '',
27659     weight: 'info',
27660     closable: false,
27661     fixed: false,
27662     beforeClass: 'bootstrap-sticky-wrap',
27663     
27664     getAutoCreate : function(){
27665         
27666         var cfg = {
27667             tag: 'div',
27668             cls: 'alert alert-dismissable alert-' + this.weight,
27669             cn: [
27670                 {
27671                     tag: 'span',
27672                     cls: 'message',
27673                     html: this.html || ''
27674                 }
27675             ]
27676         };
27677         
27678         if(this.fixed){
27679             cfg.cls += ' alert-messages-fixed';
27680         }
27681         
27682         if(this.closable){
27683             cfg.cn.push({
27684                 tag: 'button',
27685                 cls: 'close',
27686                 html: 'x'
27687             });
27688         }
27689         
27690         return cfg;
27691     },
27692     
27693     onRender : function(ct, position)
27694     {
27695         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27696         
27697         if(!this.el){
27698             var cfg = Roo.apply({},  this.getAutoCreate());
27699             cfg.id = Roo.id();
27700             
27701             if (this.cls) {
27702                 cfg.cls += ' ' + this.cls;
27703             }
27704             if (this.style) {
27705                 cfg.style = this.style;
27706             }
27707             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27708             
27709             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27710         }
27711         
27712         this.el.select('>button.close').on('click', this.hide, this);
27713         
27714     },
27715     
27716     show : function()
27717     {
27718         if (!this.rendered) {
27719             this.render();
27720         }
27721         
27722         this.el.show();
27723         
27724         this.fireEvent('show', this);
27725         
27726     },
27727     
27728     hide : function()
27729     {
27730         if (!this.rendered) {
27731             this.render();
27732         }
27733         
27734         this.el.hide();
27735         
27736         this.fireEvent('hide', this);
27737     },
27738     
27739     update : function()
27740     {
27741 //        var e = this.el.dom.firstChild;
27742 //        
27743 //        if(this.closable){
27744 //            e = e.nextSibling;
27745 //        }
27746 //        
27747 //        e.data = this.html || '';
27748
27749         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27750     }
27751    
27752 });
27753
27754  
27755
27756      /*
27757  * - LGPL
27758  *
27759  * Graph
27760  * 
27761  */
27762
27763
27764 /**
27765  * @class Roo.bootstrap.Graph
27766  * @extends Roo.bootstrap.Component
27767  * Bootstrap Graph class
27768 > Prameters
27769  -sm {number} sm 4
27770  -md {number} md 5
27771  @cfg {String} graphtype  bar | vbar | pie
27772  @cfg {number} g_x coodinator | centre x (pie)
27773  @cfg {number} g_y coodinator | centre y (pie)
27774  @cfg {number} g_r radius (pie)
27775  @cfg {number} g_height height of the chart (respected by all elements in the set)
27776  @cfg {number} g_width width of the chart (respected by all elements in the set)
27777  @cfg {Object} title The title of the chart
27778     
27779  -{Array}  values
27780  -opts (object) options for the chart 
27781      o {
27782      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27783      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27784      o vgutter (number)
27785      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.
27786      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27787      o to
27788      o stretch (boolean)
27789      o }
27790  -opts (object) options for the pie
27791      o{
27792      o cut
27793      o startAngle (number)
27794      o endAngle (number)
27795      } 
27796  *
27797  * @constructor
27798  * Create a new Input
27799  * @param {Object} config The config object
27800  */
27801
27802 Roo.bootstrap.Graph = function(config){
27803     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27804     
27805     this.addEvents({
27806         // img events
27807         /**
27808          * @event click
27809          * The img click event for the img.
27810          * @param {Roo.EventObject} e
27811          */
27812         "click" : true
27813     });
27814 };
27815
27816 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27817     
27818     sm: 4,
27819     md: 5,
27820     graphtype: 'bar',
27821     g_height: 250,
27822     g_width: 400,
27823     g_x: 50,
27824     g_y: 50,
27825     g_r: 30,
27826     opts:{
27827         //g_colors: this.colors,
27828         g_type: 'soft',
27829         g_gutter: '20%'
27830
27831     },
27832     title : false,
27833
27834     getAutoCreate : function(){
27835         
27836         var cfg = {
27837             tag: 'div',
27838             html : null
27839         };
27840         
27841         
27842         return  cfg;
27843     },
27844
27845     onRender : function(ct,position){
27846         
27847         
27848         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27849         
27850         if (typeof(Raphael) == 'undefined') {
27851             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27852             return;
27853         }
27854         
27855         this.raphael = Raphael(this.el.dom);
27856         
27857                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27858                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27859                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27860                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27861                 /*
27862                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27863                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27864                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27865                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27866                 
27867                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27868                 r.barchart(330, 10, 300, 220, data1);
27869                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27870                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27871                 */
27872                 
27873                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27874                 // r.barchart(30, 30, 560, 250,  xdata, {
27875                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27876                 //     axis : "0 0 1 1",
27877                 //     axisxlabels :  xdata
27878                 //     //yvalues : cols,
27879                    
27880                 // });
27881 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27882 //        
27883 //        this.load(null,xdata,{
27884 //                axis : "0 0 1 1",
27885 //                axisxlabels :  xdata
27886 //                });
27887
27888     },
27889
27890     load : function(graphtype,xdata,opts)
27891     {
27892         this.raphael.clear();
27893         if(!graphtype) {
27894             graphtype = this.graphtype;
27895         }
27896         if(!opts){
27897             opts = this.opts;
27898         }
27899         var r = this.raphael,
27900             fin = function () {
27901                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27902             },
27903             fout = function () {
27904                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27905             },
27906             pfin = function() {
27907                 this.sector.stop();
27908                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27909
27910                 if (this.label) {
27911                     this.label[0].stop();
27912                     this.label[0].attr({ r: 7.5 });
27913                     this.label[1].attr({ "font-weight": 800 });
27914                 }
27915             },
27916             pfout = function() {
27917                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27918
27919                 if (this.label) {
27920                     this.label[0].animate({ r: 5 }, 500, "bounce");
27921                     this.label[1].attr({ "font-weight": 400 });
27922                 }
27923             };
27924
27925         switch(graphtype){
27926             case 'bar':
27927                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27928                 break;
27929             case 'hbar':
27930                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27931                 break;
27932             case 'pie':
27933 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27934 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27935 //            
27936                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27937                 
27938                 break;
27939
27940         }
27941         
27942         if(this.title){
27943             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27944         }
27945         
27946     },
27947     
27948     setTitle: function(o)
27949     {
27950         this.title = o;
27951     },
27952     
27953     initEvents: function() {
27954         
27955         if(!this.href){
27956             this.el.on('click', this.onClick, this);
27957         }
27958     },
27959     
27960     onClick : function(e)
27961     {
27962         Roo.log('img onclick');
27963         this.fireEvent('click', this, e);
27964     }
27965    
27966 });
27967
27968  
27969 /*
27970  * - LGPL
27971  *
27972  * numberBox
27973  * 
27974  */
27975 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27976
27977 /**
27978  * @class Roo.bootstrap.dash.NumberBox
27979  * @extends Roo.bootstrap.Component
27980  * Bootstrap NumberBox class
27981  * @cfg {String} headline Box headline
27982  * @cfg {String} content Box content
27983  * @cfg {String} icon Box icon
27984  * @cfg {String} footer Footer text
27985  * @cfg {String} fhref Footer href
27986  * 
27987  * @constructor
27988  * Create a new NumberBox
27989  * @param {Object} config The config object
27990  */
27991
27992
27993 Roo.bootstrap.dash.NumberBox = function(config){
27994     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27995     
27996 };
27997
27998 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27999     
28000     headline : '',
28001     content : '',
28002     icon : '',
28003     footer : '',
28004     fhref : '',
28005     ficon : '',
28006     
28007     getAutoCreate : function(){
28008         
28009         var cfg = {
28010             tag : 'div',
28011             cls : 'small-box ',
28012             cn : [
28013                 {
28014                     tag : 'div',
28015                     cls : 'inner',
28016                     cn :[
28017                         {
28018                             tag : 'h3',
28019                             cls : 'roo-headline',
28020                             html : this.headline
28021                         },
28022                         {
28023                             tag : 'p',
28024                             cls : 'roo-content',
28025                             html : this.content
28026                         }
28027                     ]
28028                 }
28029             ]
28030         };
28031         
28032         if(this.icon){
28033             cfg.cn.push({
28034                 tag : 'div',
28035                 cls : 'icon',
28036                 cn :[
28037                     {
28038                         tag : 'i',
28039                         cls : 'ion ' + this.icon
28040                     }
28041                 ]
28042             });
28043         }
28044         
28045         if(this.footer){
28046             var footer = {
28047                 tag : 'a',
28048                 cls : 'small-box-footer',
28049                 href : this.fhref || '#',
28050                 html : this.footer
28051             };
28052             
28053             cfg.cn.push(footer);
28054             
28055         }
28056         
28057         return  cfg;
28058     },
28059
28060     onRender : function(ct,position){
28061         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28062
28063
28064        
28065                 
28066     },
28067
28068     setHeadline: function (value)
28069     {
28070         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28071     },
28072     
28073     setFooter: function (value, href)
28074     {
28075         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28076         
28077         if(href){
28078             this.el.select('a.small-box-footer',true).first().attr('href', href);
28079         }
28080         
28081     },
28082
28083     setContent: function (value)
28084     {
28085         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28086     },
28087
28088     initEvents: function() 
28089     {   
28090         
28091     }
28092     
28093 });
28094
28095  
28096 /*
28097  * - LGPL
28098  *
28099  * TabBox
28100  * 
28101  */
28102 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28103
28104 /**
28105  * @class Roo.bootstrap.dash.TabBox
28106  * @extends Roo.bootstrap.Component
28107  * Bootstrap TabBox class
28108  * @cfg {String} title Title of the TabBox
28109  * @cfg {String} icon Icon of the TabBox
28110  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28111  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28112  * 
28113  * @constructor
28114  * Create a new TabBox
28115  * @param {Object} config The config object
28116  */
28117
28118
28119 Roo.bootstrap.dash.TabBox = function(config){
28120     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28121     this.addEvents({
28122         // raw events
28123         /**
28124          * @event addpane
28125          * When a pane is added
28126          * @param {Roo.bootstrap.dash.TabPane} pane
28127          */
28128         "addpane" : true,
28129         /**
28130          * @event activatepane
28131          * When a pane is activated
28132          * @param {Roo.bootstrap.dash.TabPane} pane
28133          */
28134         "activatepane" : true
28135         
28136          
28137     });
28138     
28139     this.panes = [];
28140 };
28141
28142 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28143
28144     title : '',
28145     icon : false,
28146     showtabs : true,
28147     tabScrollable : false,
28148     
28149     getChildContainer : function()
28150     {
28151         return this.el.select('.tab-content', true).first();
28152     },
28153     
28154     getAutoCreate : function(){
28155         
28156         var header = {
28157             tag: 'li',
28158             cls: 'pull-left header',
28159             html: this.title,
28160             cn : []
28161         };
28162         
28163         if(this.icon){
28164             header.cn.push({
28165                 tag: 'i',
28166                 cls: 'fa ' + this.icon
28167             });
28168         }
28169         
28170         var h = {
28171             tag: 'ul',
28172             cls: 'nav nav-tabs pull-right',
28173             cn: [
28174                 header
28175             ]
28176         };
28177         
28178         if(this.tabScrollable){
28179             h = {
28180                 tag: 'div',
28181                 cls: 'tab-header',
28182                 cn: [
28183                     {
28184                         tag: 'ul',
28185                         cls: 'nav nav-tabs pull-right',
28186                         cn: [
28187                             header
28188                         ]
28189                     }
28190                 ]
28191             };
28192         }
28193         
28194         var cfg = {
28195             tag: 'div',
28196             cls: 'nav-tabs-custom',
28197             cn: [
28198                 h,
28199                 {
28200                     tag: 'div',
28201                     cls: 'tab-content no-padding',
28202                     cn: []
28203                 }
28204             ]
28205         };
28206
28207         return  cfg;
28208     },
28209     initEvents : function()
28210     {
28211         //Roo.log('add add pane handler');
28212         this.on('addpane', this.onAddPane, this);
28213     },
28214      /**
28215      * Updates the box title
28216      * @param {String} html to set the title to.
28217      */
28218     setTitle : function(value)
28219     {
28220         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28221     },
28222     onAddPane : function(pane)
28223     {
28224         this.panes.push(pane);
28225         //Roo.log('addpane');
28226         //Roo.log(pane);
28227         // tabs are rendere left to right..
28228         if(!this.showtabs){
28229             return;
28230         }
28231         
28232         var ctr = this.el.select('.nav-tabs', true).first();
28233          
28234          
28235         var existing = ctr.select('.nav-tab',true);
28236         var qty = existing.getCount();;
28237         
28238         
28239         var tab = ctr.createChild({
28240             tag : 'li',
28241             cls : 'nav-tab' + (qty ? '' : ' active'),
28242             cn : [
28243                 {
28244                     tag : 'a',
28245                     href:'#',
28246                     html : pane.title
28247                 }
28248             ]
28249         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28250         pane.tab = tab;
28251         
28252         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28253         if (!qty) {
28254             pane.el.addClass('active');
28255         }
28256         
28257                 
28258     },
28259     onTabClick : function(ev,un,ob,pane)
28260     {
28261         //Roo.log('tab - prev default');
28262         ev.preventDefault();
28263         
28264         
28265         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28266         pane.tab.addClass('active');
28267         //Roo.log(pane.title);
28268         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28269         // technically we should have a deactivate event.. but maybe add later.
28270         // and it should not de-activate the selected tab...
28271         this.fireEvent('activatepane', pane);
28272         pane.el.addClass('active');
28273         pane.fireEvent('activate');
28274         
28275         
28276     },
28277     
28278     getActivePane : function()
28279     {
28280         var r = false;
28281         Roo.each(this.panes, function(p) {
28282             if(p.el.hasClass('active')){
28283                 r = p;
28284                 return false;
28285             }
28286             
28287             return;
28288         });
28289         
28290         return r;
28291     }
28292     
28293     
28294 });
28295
28296  
28297 /*
28298  * - LGPL
28299  *
28300  * Tab pane
28301  * 
28302  */
28303 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28304 /**
28305  * @class Roo.bootstrap.TabPane
28306  * @extends Roo.bootstrap.Component
28307  * Bootstrap TabPane class
28308  * @cfg {Boolean} active (false | true) Default false
28309  * @cfg {String} title title of panel
28310
28311  * 
28312  * @constructor
28313  * Create a new TabPane
28314  * @param {Object} config The config object
28315  */
28316
28317 Roo.bootstrap.dash.TabPane = function(config){
28318     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28319     
28320     this.addEvents({
28321         // raw events
28322         /**
28323          * @event activate
28324          * When a pane is activated
28325          * @param {Roo.bootstrap.dash.TabPane} pane
28326          */
28327         "activate" : true
28328          
28329     });
28330 };
28331
28332 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28333     
28334     active : false,
28335     title : '',
28336     
28337     // the tabBox that this is attached to.
28338     tab : false,
28339      
28340     getAutoCreate : function() 
28341     {
28342         var cfg = {
28343             tag: 'div',
28344             cls: 'tab-pane'
28345         };
28346         
28347         if(this.active){
28348             cfg.cls += ' active';
28349         }
28350         
28351         return cfg;
28352     },
28353     initEvents  : function()
28354     {
28355         //Roo.log('trigger add pane handler');
28356         this.parent().fireEvent('addpane', this)
28357     },
28358     
28359      /**
28360      * Updates the tab title 
28361      * @param {String} html to set the title to.
28362      */
28363     setTitle: function(str)
28364     {
28365         if (!this.tab) {
28366             return;
28367         }
28368         this.title = str;
28369         this.tab.select('a', true).first().dom.innerHTML = str;
28370         
28371     }
28372     
28373     
28374     
28375 });
28376
28377  
28378
28379
28380  /*
28381  * - LGPL
28382  *
28383  * menu
28384  * 
28385  */
28386 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28387
28388 /**
28389  * @class Roo.bootstrap.menu.Menu
28390  * @extends Roo.bootstrap.Component
28391  * Bootstrap Menu class - container for Menu
28392  * @cfg {String} html Text of the menu
28393  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28394  * @cfg {String} icon Font awesome icon
28395  * @cfg {String} pos Menu align to (top | bottom) default bottom
28396  * 
28397  * 
28398  * @constructor
28399  * Create a new Menu
28400  * @param {Object} config The config object
28401  */
28402
28403
28404 Roo.bootstrap.menu.Menu = function(config){
28405     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28406     
28407     this.addEvents({
28408         /**
28409          * @event beforeshow
28410          * Fires before this menu is displayed
28411          * @param {Roo.bootstrap.menu.Menu} this
28412          */
28413         beforeshow : true,
28414         /**
28415          * @event beforehide
28416          * Fires before this menu is hidden
28417          * @param {Roo.bootstrap.menu.Menu} this
28418          */
28419         beforehide : true,
28420         /**
28421          * @event show
28422          * Fires after this menu is displayed
28423          * @param {Roo.bootstrap.menu.Menu} this
28424          */
28425         show : true,
28426         /**
28427          * @event hide
28428          * Fires after this menu is hidden
28429          * @param {Roo.bootstrap.menu.Menu} this
28430          */
28431         hide : true,
28432         /**
28433          * @event click
28434          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28435          * @param {Roo.bootstrap.menu.Menu} this
28436          * @param {Roo.EventObject} e
28437          */
28438         click : true
28439     });
28440     
28441 };
28442
28443 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28444     
28445     submenu : false,
28446     html : '',
28447     weight : 'default',
28448     icon : false,
28449     pos : 'bottom',
28450     
28451     
28452     getChildContainer : function() {
28453         if(this.isSubMenu){
28454             return this.el;
28455         }
28456         
28457         return this.el.select('ul.dropdown-menu', true).first();  
28458     },
28459     
28460     getAutoCreate : function()
28461     {
28462         var text = [
28463             {
28464                 tag : 'span',
28465                 cls : 'roo-menu-text',
28466                 html : this.html
28467             }
28468         ];
28469         
28470         if(this.icon){
28471             text.unshift({
28472                 tag : 'i',
28473                 cls : 'fa ' + this.icon
28474             })
28475         }
28476         
28477         
28478         var cfg = {
28479             tag : 'div',
28480             cls : 'btn-group',
28481             cn : [
28482                 {
28483                     tag : 'button',
28484                     cls : 'dropdown-button btn btn-' + this.weight,
28485                     cn : text
28486                 },
28487                 {
28488                     tag : 'button',
28489                     cls : 'dropdown-toggle btn btn-' + this.weight,
28490                     cn : [
28491                         {
28492                             tag : 'span',
28493                             cls : 'caret'
28494                         }
28495                     ]
28496                 },
28497                 {
28498                     tag : 'ul',
28499                     cls : 'dropdown-menu'
28500                 }
28501             ]
28502             
28503         };
28504         
28505         if(this.pos == 'top'){
28506             cfg.cls += ' dropup';
28507         }
28508         
28509         if(this.isSubMenu){
28510             cfg = {
28511                 tag : 'ul',
28512                 cls : 'dropdown-menu'
28513             }
28514         }
28515         
28516         return cfg;
28517     },
28518     
28519     onRender : function(ct, position)
28520     {
28521         this.isSubMenu = ct.hasClass('dropdown-submenu');
28522         
28523         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28524     },
28525     
28526     initEvents : function() 
28527     {
28528         if(this.isSubMenu){
28529             return;
28530         }
28531         
28532         this.hidden = true;
28533         
28534         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28535         this.triggerEl.on('click', this.onTriggerPress, this);
28536         
28537         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28538         this.buttonEl.on('click', this.onClick, this);
28539         
28540     },
28541     
28542     list : function()
28543     {
28544         if(this.isSubMenu){
28545             return this.el;
28546         }
28547         
28548         return this.el.select('ul.dropdown-menu', true).first();
28549     },
28550     
28551     onClick : function(e)
28552     {
28553         this.fireEvent("click", this, e);
28554     },
28555     
28556     onTriggerPress  : function(e)
28557     {   
28558         if (this.isVisible()) {
28559             this.hide();
28560         } else {
28561             this.show();
28562         }
28563     },
28564     
28565     isVisible : function(){
28566         return !this.hidden;
28567     },
28568     
28569     show : function()
28570     {
28571         this.fireEvent("beforeshow", this);
28572         
28573         this.hidden = false;
28574         this.el.addClass('open');
28575         
28576         Roo.get(document).on("mouseup", this.onMouseUp, this);
28577         
28578         this.fireEvent("show", this);
28579         
28580         
28581     },
28582     
28583     hide : function()
28584     {
28585         this.fireEvent("beforehide", this);
28586         
28587         this.hidden = true;
28588         this.el.removeClass('open');
28589         
28590         Roo.get(document).un("mouseup", this.onMouseUp);
28591         
28592         this.fireEvent("hide", this);
28593     },
28594     
28595     onMouseUp : function()
28596     {
28597         this.hide();
28598     }
28599     
28600 });
28601
28602  
28603  /*
28604  * - LGPL
28605  *
28606  * menu item
28607  * 
28608  */
28609 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28610
28611 /**
28612  * @class Roo.bootstrap.menu.Item
28613  * @extends Roo.bootstrap.Component
28614  * Bootstrap MenuItem class
28615  * @cfg {Boolean} submenu (true | false) default false
28616  * @cfg {String} html text of the item
28617  * @cfg {String} href the link
28618  * @cfg {Boolean} disable (true | false) default false
28619  * @cfg {Boolean} preventDefault (true | false) default true
28620  * @cfg {String} icon Font awesome icon
28621  * @cfg {String} pos Submenu align to (left | right) default right 
28622  * 
28623  * 
28624  * @constructor
28625  * Create a new Item
28626  * @param {Object} config The config object
28627  */
28628
28629
28630 Roo.bootstrap.menu.Item = function(config){
28631     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28632     this.addEvents({
28633         /**
28634          * @event mouseover
28635          * Fires when the mouse is hovering over this menu
28636          * @param {Roo.bootstrap.menu.Item} this
28637          * @param {Roo.EventObject} e
28638          */
28639         mouseover : true,
28640         /**
28641          * @event mouseout
28642          * Fires when the mouse exits this menu
28643          * @param {Roo.bootstrap.menu.Item} this
28644          * @param {Roo.EventObject} e
28645          */
28646         mouseout : true,
28647         // raw events
28648         /**
28649          * @event click
28650          * The raw click event for the entire grid.
28651          * @param {Roo.EventObject} e
28652          */
28653         click : true
28654     });
28655 };
28656
28657 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28658     
28659     submenu : false,
28660     href : '',
28661     html : '',
28662     preventDefault: true,
28663     disable : false,
28664     icon : false,
28665     pos : 'right',
28666     
28667     getAutoCreate : function()
28668     {
28669         var text = [
28670             {
28671                 tag : 'span',
28672                 cls : 'roo-menu-item-text',
28673                 html : this.html
28674             }
28675         ];
28676         
28677         if(this.icon){
28678             text.unshift({
28679                 tag : 'i',
28680                 cls : 'fa ' + this.icon
28681             })
28682         }
28683         
28684         var cfg = {
28685             tag : 'li',
28686             cn : [
28687                 {
28688                     tag : 'a',
28689                     href : this.href || '#',
28690                     cn : text
28691                 }
28692             ]
28693         };
28694         
28695         if(this.disable){
28696             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28697         }
28698         
28699         if(this.submenu){
28700             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28701             
28702             if(this.pos == 'left'){
28703                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28704             }
28705         }
28706         
28707         return cfg;
28708     },
28709     
28710     initEvents : function() 
28711     {
28712         this.el.on('mouseover', this.onMouseOver, this);
28713         this.el.on('mouseout', this.onMouseOut, this);
28714         
28715         this.el.select('a', true).first().on('click', this.onClick, this);
28716         
28717     },
28718     
28719     onClick : function(e)
28720     {
28721         if(this.preventDefault){
28722             e.preventDefault();
28723         }
28724         
28725         this.fireEvent("click", this, e);
28726     },
28727     
28728     onMouseOver : function(e)
28729     {
28730         if(this.submenu && this.pos == 'left'){
28731             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28732         }
28733         
28734         this.fireEvent("mouseover", this, e);
28735     },
28736     
28737     onMouseOut : function(e)
28738     {
28739         this.fireEvent("mouseout", this, e);
28740     }
28741 });
28742
28743  
28744
28745  /*
28746  * - LGPL
28747  *
28748  * menu separator
28749  * 
28750  */
28751 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28752
28753 /**
28754  * @class Roo.bootstrap.menu.Separator
28755  * @extends Roo.bootstrap.Component
28756  * Bootstrap Separator class
28757  * 
28758  * @constructor
28759  * Create a new Separator
28760  * @param {Object} config The config object
28761  */
28762
28763
28764 Roo.bootstrap.menu.Separator = function(config){
28765     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28766 };
28767
28768 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28769     
28770     getAutoCreate : function(){
28771         var cfg = {
28772             tag : 'li',
28773             cls: 'divider'
28774         };
28775         
28776         return cfg;
28777     }
28778    
28779 });
28780
28781  
28782
28783  /*
28784  * - LGPL
28785  *
28786  * Tooltip
28787  * 
28788  */
28789
28790 /**
28791  * @class Roo.bootstrap.Tooltip
28792  * Bootstrap Tooltip class
28793  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28794  * to determine which dom element triggers the tooltip.
28795  * 
28796  * It needs to add support for additional attributes like tooltip-position
28797  * 
28798  * @constructor
28799  * Create a new Toolti
28800  * @param {Object} config The config object
28801  */
28802
28803 Roo.bootstrap.Tooltip = function(config){
28804     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28805     
28806     this.alignment = Roo.bootstrap.Tooltip.alignment;
28807     
28808     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28809         this.alignment = config.alignment;
28810     }
28811     
28812 };
28813
28814 Roo.apply(Roo.bootstrap.Tooltip, {
28815     /**
28816      * @function init initialize tooltip monitoring.
28817      * @static
28818      */
28819     currentEl : false,
28820     currentTip : false,
28821     currentRegion : false,
28822     
28823     //  init : delay?
28824     
28825     init : function()
28826     {
28827         Roo.get(document).on('mouseover', this.enter ,this);
28828         Roo.get(document).on('mouseout', this.leave, this);
28829          
28830         
28831         this.currentTip = new Roo.bootstrap.Tooltip();
28832     },
28833     
28834     enter : function(ev)
28835     {
28836         var dom = ev.getTarget();
28837         
28838         //Roo.log(['enter',dom]);
28839         var el = Roo.fly(dom);
28840         if (this.currentEl) {
28841             //Roo.log(dom);
28842             //Roo.log(this.currentEl);
28843             //Roo.log(this.currentEl.contains(dom));
28844             if (this.currentEl == el) {
28845                 return;
28846             }
28847             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28848                 return;
28849             }
28850
28851         }
28852         
28853         if (this.currentTip.el) {
28854             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28855         }    
28856         //Roo.log(ev);
28857         
28858         if(!el || el.dom == document){
28859             return;
28860         }
28861         
28862         var bindEl = el;
28863         
28864         // you can not look for children, as if el is the body.. then everythign is the child..
28865         if (!el.attr('tooltip')) { //
28866             if (!el.select("[tooltip]").elements.length) {
28867                 return;
28868             }
28869             // is the mouse over this child...?
28870             bindEl = el.select("[tooltip]").first();
28871             var xy = ev.getXY();
28872             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28873                 //Roo.log("not in region.");
28874                 return;
28875             }
28876             //Roo.log("child element over..");
28877             
28878         }
28879         this.currentEl = bindEl;
28880         this.currentTip.bind(bindEl);
28881         this.currentRegion = Roo.lib.Region.getRegion(dom);
28882         this.currentTip.enter();
28883         
28884     },
28885     leave : function(ev)
28886     {
28887         var dom = ev.getTarget();
28888         //Roo.log(['leave',dom]);
28889         if (!this.currentEl) {
28890             return;
28891         }
28892         
28893         
28894         if (dom != this.currentEl.dom) {
28895             return;
28896         }
28897         var xy = ev.getXY();
28898         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28899             return;
28900         }
28901         // only activate leave if mouse cursor is outside... bounding box..
28902         
28903         
28904         
28905         
28906         if (this.currentTip) {
28907             this.currentTip.leave();
28908         }
28909         //Roo.log('clear currentEl');
28910         this.currentEl = false;
28911         
28912         
28913     },
28914     alignment : {
28915         'left' : ['r-l', [-2,0], 'right'],
28916         'right' : ['l-r', [2,0], 'left'],
28917         'bottom' : ['t-b', [0,2], 'top'],
28918         'top' : [ 'b-t', [0,-2], 'bottom']
28919     }
28920     
28921 });
28922
28923
28924 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28925     
28926     
28927     bindEl : false,
28928     
28929     delay : null, // can be { show : 300 , hide: 500}
28930     
28931     timeout : null,
28932     
28933     hoverState : null, //???
28934     
28935     placement : 'bottom', 
28936     
28937     alignment : false,
28938     
28939     getAutoCreate : function(){
28940     
28941         var cfg = {
28942            cls : 'tooltip',   
28943            role : 'tooltip',
28944            cn : [
28945                 {
28946                     cls : 'tooltip-arrow arrow'
28947                 },
28948                 {
28949                     cls : 'tooltip-inner'
28950                 }
28951            ]
28952         };
28953         
28954         return cfg;
28955     },
28956     bind : function(el)
28957     {
28958         this.bindEl = el;
28959     },
28960     
28961     initEvents : function()
28962     {
28963         this.arrowEl = this.el.select('.arrow', true).first();
28964         this.innerEl = this.el.select('.tooltip-inner', true).first();
28965     },
28966     
28967     enter : function () {
28968        
28969         if (this.timeout != null) {
28970             clearTimeout(this.timeout);
28971         }
28972         
28973         this.hoverState = 'in';
28974          //Roo.log("enter - show");
28975         if (!this.delay || !this.delay.show) {
28976             this.show();
28977             return;
28978         }
28979         var _t = this;
28980         this.timeout = setTimeout(function () {
28981             if (_t.hoverState == 'in') {
28982                 _t.show();
28983             }
28984         }, this.delay.show);
28985     },
28986     leave : function()
28987     {
28988         clearTimeout(this.timeout);
28989     
28990         this.hoverState = 'out';
28991          if (!this.delay || !this.delay.hide) {
28992             this.hide();
28993             return;
28994         }
28995        
28996         var _t = this;
28997         this.timeout = setTimeout(function () {
28998             //Roo.log("leave - timeout");
28999             
29000             if (_t.hoverState == 'out') {
29001                 _t.hide();
29002                 Roo.bootstrap.Tooltip.currentEl = false;
29003             }
29004         }, delay);
29005     },
29006     
29007     show : function (msg)
29008     {
29009         if (!this.el) {
29010             this.render(document.body);
29011         }
29012         // set content.
29013         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29014         
29015         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29016         
29017         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29018         
29019         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29020                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29021         
29022         var placement = typeof this.placement == 'function' ?
29023             this.placement.call(this, this.el, on_el) :
29024             this.placement;
29025             
29026         var autoToken = /\s?auto?\s?/i;
29027         var autoPlace = autoToken.test(placement);
29028         if (autoPlace) {
29029             placement = placement.replace(autoToken, '') || 'top';
29030         }
29031         
29032         //this.el.detach()
29033         //this.el.setXY([0,0]);
29034         this.el.show();
29035         //this.el.dom.style.display='block';
29036         
29037         //this.el.appendTo(on_el);
29038         
29039         var p = this.getPosition();
29040         var box = this.el.getBox();
29041         
29042         if (autoPlace) {
29043             // fixme..
29044         }
29045         
29046         var align = this.alignment[placement];
29047         
29048         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29049         
29050         if(placement == 'top' || placement == 'bottom'){
29051             if(xy[0] < 0){
29052                 placement = 'right';
29053             }
29054             
29055             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29056                 placement = 'left';
29057             }
29058             
29059             var scroll = Roo.select('body', true).first().getScroll();
29060             
29061             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29062                 placement = 'top';
29063             }
29064             
29065             align = this.alignment[placement];
29066             
29067             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29068             
29069         }
29070         
29071         this.el.alignTo(this.bindEl, align[0],align[1]);
29072         //var arrow = this.el.select('.arrow',true).first();
29073         //arrow.set(align[2], 
29074         
29075         this.el.addClass(placement);
29076         this.el.addClass("bs-tooltip-"+ placement);
29077         
29078         this.el.addClass('in fade show');
29079         
29080         this.hoverState = null;
29081         
29082         if (this.el.hasClass('fade')) {
29083             // fade it?
29084         }
29085         
29086         
29087         
29088         
29089         
29090     },
29091     hide : function()
29092     {
29093          
29094         if (!this.el) {
29095             return;
29096         }
29097         //this.el.setXY([0,0]);
29098         this.el.removeClass(['show', 'in']);
29099         //this.el.hide();
29100         
29101     }
29102     
29103 });
29104  
29105
29106  /*
29107  * - LGPL
29108  *
29109  * Location Picker
29110  * 
29111  */
29112
29113 /**
29114  * @class Roo.bootstrap.LocationPicker
29115  * @extends Roo.bootstrap.Component
29116  * Bootstrap LocationPicker class
29117  * @cfg {Number} latitude Position when init default 0
29118  * @cfg {Number} longitude Position when init default 0
29119  * @cfg {Number} zoom default 15
29120  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29121  * @cfg {Boolean} mapTypeControl default false
29122  * @cfg {Boolean} disableDoubleClickZoom default false
29123  * @cfg {Boolean} scrollwheel default true
29124  * @cfg {Boolean} streetViewControl default false
29125  * @cfg {Number} radius default 0
29126  * @cfg {String} locationName
29127  * @cfg {Boolean} draggable default true
29128  * @cfg {Boolean} enableAutocomplete default false
29129  * @cfg {Boolean} enableReverseGeocode default true
29130  * @cfg {String} markerTitle
29131  * 
29132  * @constructor
29133  * Create a new LocationPicker
29134  * @param {Object} config The config object
29135  */
29136
29137
29138 Roo.bootstrap.LocationPicker = function(config){
29139     
29140     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29141     
29142     this.addEvents({
29143         /**
29144          * @event initial
29145          * Fires when the picker initialized.
29146          * @param {Roo.bootstrap.LocationPicker} this
29147          * @param {Google Location} location
29148          */
29149         initial : true,
29150         /**
29151          * @event positionchanged
29152          * Fires when the picker position changed.
29153          * @param {Roo.bootstrap.LocationPicker} this
29154          * @param {Google Location} location
29155          */
29156         positionchanged : true,
29157         /**
29158          * @event resize
29159          * Fires when the map resize.
29160          * @param {Roo.bootstrap.LocationPicker} this
29161          */
29162         resize : true,
29163         /**
29164          * @event show
29165          * Fires when the map show.
29166          * @param {Roo.bootstrap.LocationPicker} this
29167          */
29168         show : true,
29169         /**
29170          * @event hide
29171          * Fires when the map hide.
29172          * @param {Roo.bootstrap.LocationPicker} this
29173          */
29174         hide : true,
29175         /**
29176          * @event mapClick
29177          * Fires when click the map.
29178          * @param {Roo.bootstrap.LocationPicker} this
29179          * @param {Map event} e
29180          */
29181         mapClick : true,
29182         /**
29183          * @event mapRightClick
29184          * Fires when right click the map.
29185          * @param {Roo.bootstrap.LocationPicker} this
29186          * @param {Map event} e
29187          */
29188         mapRightClick : true,
29189         /**
29190          * @event markerClick
29191          * Fires when click the marker.
29192          * @param {Roo.bootstrap.LocationPicker} this
29193          * @param {Map event} e
29194          */
29195         markerClick : true,
29196         /**
29197          * @event markerRightClick
29198          * Fires when right click the marker.
29199          * @param {Roo.bootstrap.LocationPicker} this
29200          * @param {Map event} e
29201          */
29202         markerRightClick : true,
29203         /**
29204          * @event OverlayViewDraw
29205          * Fires when OverlayView Draw
29206          * @param {Roo.bootstrap.LocationPicker} this
29207          */
29208         OverlayViewDraw : true,
29209         /**
29210          * @event OverlayViewOnAdd
29211          * Fires when OverlayView Draw
29212          * @param {Roo.bootstrap.LocationPicker} this
29213          */
29214         OverlayViewOnAdd : true,
29215         /**
29216          * @event OverlayViewOnRemove
29217          * Fires when OverlayView Draw
29218          * @param {Roo.bootstrap.LocationPicker} this
29219          */
29220         OverlayViewOnRemove : true,
29221         /**
29222          * @event OverlayViewShow
29223          * Fires when OverlayView Draw
29224          * @param {Roo.bootstrap.LocationPicker} this
29225          * @param {Pixel} cpx
29226          */
29227         OverlayViewShow : true,
29228         /**
29229          * @event OverlayViewHide
29230          * Fires when OverlayView Draw
29231          * @param {Roo.bootstrap.LocationPicker} this
29232          */
29233         OverlayViewHide : true,
29234         /**
29235          * @event loadexception
29236          * Fires when load google lib failed.
29237          * @param {Roo.bootstrap.LocationPicker} this
29238          */
29239         loadexception : true
29240     });
29241         
29242 };
29243
29244 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29245     
29246     gMapContext: false,
29247     
29248     latitude: 0,
29249     longitude: 0,
29250     zoom: 15,
29251     mapTypeId: false,
29252     mapTypeControl: false,
29253     disableDoubleClickZoom: false,
29254     scrollwheel: true,
29255     streetViewControl: false,
29256     radius: 0,
29257     locationName: '',
29258     draggable: true,
29259     enableAutocomplete: false,
29260     enableReverseGeocode: true,
29261     markerTitle: '',
29262     
29263     getAutoCreate: function()
29264     {
29265
29266         var cfg = {
29267             tag: 'div',
29268             cls: 'roo-location-picker'
29269         };
29270         
29271         return cfg
29272     },
29273     
29274     initEvents: function(ct, position)
29275     {       
29276         if(!this.el.getWidth() || this.isApplied()){
29277             return;
29278         }
29279         
29280         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29281         
29282         this.initial();
29283     },
29284     
29285     initial: function()
29286     {
29287         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29288             this.fireEvent('loadexception', this);
29289             return;
29290         }
29291         
29292         if(!this.mapTypeId){
29293             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29294         }
29295         
29296         this.gMapContext = this.GMapContext();
29297         
29298         this.initOverlayView();
29299         
29300         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29301         
29302         var _this = this;
29303                 
29304         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29305             _this.setPosition(_this.gMapContext.marker.position);
29306         });
29307         
29308         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29309             _this.fireEvent('mapClick', this, event);
29310             
29311         });
29312
29313         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29314             _this.fireEvent('mapRightClick', this, event);
29315             
29316         });
29317         
29318         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29319             _this.fireEvent('markerClick', this, event);
29320             
29321         });
29322
29323         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29324             _this.fireEvent('markerRightClick', this, event);
29325             
29326         });
29327         
29328         this.setPosition(this.gMapContext.location);
29329         
29330         this.fireEvent('initial', this, this.gMapContext.location);
29331     },
29332     
29333     initOverlayView: function()
29334     {
29335         var _this = this;
29336         
29337         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29338             
29339             draw: function()
29340             {
29341                 _this.fireEvent('OverlayViewDraw', _this);
29342             },
29343             
29344             onAdd: function()
29345             {
29346                 _this.fireEvent('OverlayViewOnAdd', _this);
29347             },
29348             
29349             onRemove: function()
29350             {
29351                 _this.fireEvent('OverlayViewOnRemove', _this);
29352             },
29353             
29354             show: function(cpx)
29355             {
29356                 _this.fireEvent('OverlayViewShow', _this, cpx);
29357             },
29358             
29359             hide: function()
29360             {
29361                 _this.fireEvent('OverlayViewHide', _this);
29362             }
29363             
29364         });
29365     },
29366     
29367     fromLatLngToContainerPixel: function(event)
29368     {
29369         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29370     },
29371     
29372     isApplied: function() 
29373     {
29374         return this.getGmapContext() == false ? false : true;
29375     },
29376     
29377     getGmapContext: function() 
29378     {
29379         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29380     },
29381     
29382     GMapContext: function() 
29383     {
29384         var position = new google.maps.LatLng(this.latitude, this.longitude);
29385         
29386         var _map = new google.maps.Map(this.el.dom, {
29387             center: position,
29388             zoom: this.zoom,
29389             mapTypeId: this.mapTypeId,
29390             mapTypeControl: this.mapTypeControl,
29391             disableDoubleClickZoom: this.disableDoubleClickZoom,
29392             scrollwheel: this.scrollwheel,
29393             streetViewControl: this.streetViewControl,
29394             locationName: this.locationName,
29395             draggable: this.draggable,
29396             enableAutocomplete: this.enableAutocomplete,
29397             enableReverseGeocode: this.enableReverseGeocode
29398         });
29399         
29400         var _marker = new google.maps.Marker({
29401             position: position,
29402             map: _map,
29403             title: this.markerTitle,
29404             draggable: this.draggable
29405         });
29406         
29407         return {
29408             map: _map,
29409             marker: _marker,
29410             circle: null,
29411             location: position,
29412             radius: this.radius,
29413             locationName: this.locationName,
29414             addressComponents: {
29415                 formatted_address: null,
29416                 addressLine1: null,
29417                 addressLine2: null,
29418                 streetName: null,
29419                 streetNumber: null,
29420                 city: null,
29421                 district: null,
29422                 state: null,
29423                 stateOrProvince: null
29424             },
29425             settings: this,
29426             domContainer: this.el.dom,
29427             geodecoder: new google.maps.Geocoder()
29428         };
29429     },
29430     
29431     drawCircle: function(center, radius, options) 
29432     {
29433         if (this.gMapContext.circle != null) {
29434             this.gMapContext.circle.setMap(null);
29435         }
29436         if (radius > 0) {
29437             radius *= 1;
29438             options = Roo.apply({}, options, {
29439                 strokeColor: "#0000FF",
29440                 strokeOpacity: .35,
29441                 strokeWeight: 2,
29442                 fillColor: "#0000FF",
29443                 fillOpacity: .2
29444             });
29445             
29446             options.map = this.gMapContext.map;
29447             options.radius = radius;
29448             options.center = center;
29449             this.gMapContext.circle = new google.maps.Circle(options);
29450             return this.gMapContext.circle;
29451         }
29452         
29453         return null;
29454     },
29455     
29456     setPosition: function(location) 
29457     {
29458         this.gMapContext.location = location;
29459         this.gMapContext.marker.setPosition(location);
29460         this.gMapContext.map.panTo(location);
29461         this.drawCircle(location, this.gMapContext.radius, {});
29462         
29463         var _this = this;
29464         
29465         if (this.gMapContext.settings.enableReverseGeocode) {
29466             this.gMapContext.geodecoder.geocode({
29467                 latLng: this.gMapContext.location
29468             }, function(results, status) {
29469                 
29470                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29471                     _this.gMapContext.locationName = results[0].formatted_address;
29472                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29473                     
29474                     _this.fireEvent('positionchanged', this, location);
29475                 }
29476             });
29477             
29478             return;
29479         }
29480         
29481         this.fireEvent('positionchanged', this, location);
29482     },
29483     
29484     resize: function()
29485     {
29486         google.maps.event.trigger(this.gMapContext.map, "resize");
29487         
29488         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29489         
29490         this.fireEvent('resize', this);
29491     },
29492     
29493     setPositionByLatLng: function(latitude, longitude)
29494     {
29495         this.setPosition(new google.maps.LatLng(latitude, longitude));
29496     },
29497     
29498     getCurrentPosition: function() 
29499     {
29500         return {
29501             latitude: this.gMapContext.location.lat(),
29502             longitude: this.gMapContext.location.lng()
29503         };
29504     },
29505     
29506     getAddressName: function() 
29507     {
29508         return this.gMapContext.locationName;
29509     },
29510     
29511     getAddressComponents: function() 
29512     {
29513         return this.gMapContext.addressComponents;
29514     },
29515     
29516     address_component_from_google_geocode: function(address_components) 
29517     {
29518         var result = {};
29519         
29520         for (var i = 0; i < address_components.length; i++) {
29521             var component = address_components[i];
29522             if (component.types.indexOf("postal_code") >= 0) {
29523                 result.postalCode = component.short_name;
29524             } else if (component.types.indexOf("street_number") >= 0) {
29525                 result.streetNumber = component.short_name;
29526             } else if (component.types.indexOf("route") >= 0) {
29527                 result.streetName = component.short_name;
29528             } else if (component.types.indexOf("neighborhood") >= 0) {
29529                 result.city = component.short_name;
29530             } else if (component.types.indexOf("locality") >= 0) {
29531                 result.city = component.short_name;
29532             } else if (component.types.indexOf("sublocality") >= 0) {
29533                 result.district = component.short_name;
29534             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29535                 result.stateOrProvince = component.short_name;
29536             } else if (component.types.indexOf("country") >= 0) {
29537                 result.country = component.short_name;
29538             }
29539         }
29540         
29541         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29542         result.addressLine2 = "";
29543         return result;
29544     },
29545     
29546     setZoomLevel: function(zoom)
29547     {
29548         this.gMapContext.map.setZoom(zoom);
29549     },
29550     
29551     show: function()
29552     {
29553         if(!this.el){
29554             return;
29555         }
29556         
29557         this.el.show();
29558         
29559         this.resize();
29560         
29561         this.fireEvent('show', this);
29562     },
29563     
29564     hide: function()
29565     {
29566         if(!this.el){
29567             return;
29568         }
29569         
29570         this.el.hide();
29571         
29572         this.fireEvent('hide', this);
29573     }
29574     
29575 });
29576
29577 Roo.apply(Roo.bootstrap.LocationPicker, {
29578     
29579     OverlayView : function(map, options)
29580     {
29581         options = options || {};
29582         
29583         this.setMap(map);
29584     }
29585     
29586     
29587 });/**
29588  * @class Roo.bootstrap.Alert
29589  * @extends Roo.bootstrap.Component
29590  * Bootstrap Alert class - shows an alert area box
29591  * eg
29592  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29593   Enter a valid email address
29594 </div>
29595  * @licence LGPL
29596  * @cfg {String} title The title of alert
29597  * @cfg {String} html The content of alert
29598  * @cfg {String} weight (  success | info | warning | danger )
29599  * @cfg {String} faicon font-awesomeicon
29600  * 
29601  * @constructor
29602  * Create a new alert
29603  * @param {Object} config The config object
29604  */
29605
29606
29607 Roo.bootstrap.Alert = function(config){
29608     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29609     
29610 };
29611
29612 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29613     
29614     title: '',
29615     html: '',
29616     weight: false,
29617     faicon: false,
29618     
29619     getAutoCreate : function()
29620     {
29621         
29622         var cfg = {
29623             tag : 'div',
29624             cls : 'alert',
29625             cn : [
29626                 {
29627                     tag : 'i',
29628                     cls : 'roo-alert-icon'
29629                     
29630                 },
29631                 {
29632                     tag : 'b',
29633                     cls : 'roo-alert-title',
29634                     html : this.title
29635                 },
29636                 {
29637                     tag : 'span',
29638                     cls : 'roo-alert-text',
29639                     html : this.html
29640                 }
29641             ]
29642         };
29643         
29644         if(this.faicon){
29645             cfg.cn[0].cls += ' fa ' + this.faicon;
29646         }
29647         
29648         if(this.weight){
29649             cfg.cls += ' alert-' + this.weight;
29650         }
29651         
29652         return cfg;
29653     },
29654     
29655     initEvents: function() 
29656     {
29657         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29658     },
29659     
29660     setTitle : function(str)
29661     {
29662         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29663     },
29664     
29665     setText : function(str)
29666     {
29667         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29668     },
29669     
29670     setWeight : function(weight)
29671     {
29672         if(this.weight){
29673             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29674         }
29675         
29676         this.weight = weight;
29677         
29678         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29679     },
29680     
29681     setIcon : function(icon)
29682     {
29683         if(this.faicon){
29684             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29685         }
29686         
29687         this.faicon = icon;
29688         
29689         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29690     },
29691     
29692     hide: function() 
29693     {
29694         this.el.hide();   
29695     },
29696     
29697     show: function() 
29698     {  
29699         this.el.show();   
29700     }
29701     
29702 });
29703
29704  
29705 /*
29706 * Licence: LGPL
29707 */
29708
29709 /**
29710  * @class Roo.bootstrap.UploadCropbox
29711  * @extends Roo.bootstrap.Component
29712  * Bootstrap UploadCropbox class
29713  * @cfg {String} emptyText show when image has been loaded
29714  * @cfg {String} rotateNotify show when image too small to rotate
29715  * @cfg {Number} errorTimeout default 3000
29716  * @cfg {Number} minWidth default 300
29717  * @cfg {Number} minHeight default 300
29718  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29719  * @cfg {Boolean} isDocument (true|false) default false
29720  * @cfg {String} url action url
29721  * @cfg {String} paramName default 'imageUpload'
29722  * @cfg {String} method default POST
29723  * @cfg {Boolean} loadMask (true|false) default true
29724  * @cfg {Boolean} loadingText default 'Loading...'
29725  * 
29726  * @constructor
29727  * Create a new UploadCropbox
29728  * @param {Object} config The config object
29729  */
29730
29731 Roo.bootstrap.UploadCropbox = function(config){
29732     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29733     
29734     this.addEvents({
29735         /**
29736          * @event beforeselectfile
29737          * Fire before select file
29738          * @param {Roo.bootstrap.UploadCropbox} this
29739          */
29740         "beforeselectfile" : true,
29741         /**
29742          * @event initial
29743          * Fire after initEvent
29744          * @param {Roo.bootstrap.UploadCropbox} this
29745          */
29746         "initial" : true,
29747         /**
29748          * @event crop
29749          * Fire after initEvent
29750          * @param {Roo.bootstrap.UploadCropbox} this
29751          * @param {String} data
29752          */
29753         "crop" : true,
29754         /**
29755          * @event prepare
29756          * Fire when preparing the file data
29757          * @param {Roo.bootstrap.UploadCropbox} this
29758          * @param {Object} file
29759          */
29760         "prepare" : true,
29761         /**
29762          * @event exception
29763          * Fire when get exception
29764          * @param {Roo.bootstrap.UploadCropbox} this
29765          * @param {XMLHttpRequest} xhr
29766          */
29767         "exception" : true,
29768         /**
29769          * @event beforeloadcanvas
29770          * Fire before load the canvas
29771          * @param {Roo.bootstrap.UploadCropbox} this
29772          * @param {String} src
29773          */
29774         "beforeloadcanvas" : true,
29775         /**
29776          * @event trash
29777          * Fire when trash image
29778          * @param {Roo.bootstrap.UploadCropbox} this
29779          */
29780         "trash" : true,
29781         /**
29782          * @event download
29783          * Fire when download the image
29784          * @param {Roo.bootstrap.UploadCropbox} this
29785          */
29786         "download" : true,
29787         /**
29788          * @event footerbuttonclick
29789          * Fire when footerbuttonclick
29790          * @param {Roo.bootstrap.UploadCropbox} this
29791          * @param {String} type
29792          */
29793         "footerbuttonclick" : true,
29794         /**
29795          * @event resize
29796          * Fire when resize
29797          * @param {Roo.bootstrap.UploadCropbox} this
29798          */
29799         "resize" : true,
29800         /**
29801          * @event rotate
29802          * Fire when rotate the image
29803          * @param {Roo.bootstrap.UploadCropbox} this
29804          * @param {String} pos
29805          */
29806         "rotate" : true,
29807         /**
29808          * @event inspect
29809          * Fire when inspect the file
29810          * @param {Roo.bootstrap.UploadCropbox} this
29811          * @param {Object} file
29812          */
29813         "inspect" : true,
29814         /**
29815          * @event upload
29816          * Fire when xhr upload the file
29817          * @param {Roo.bootstrap.UploadCropbox} this
29818          * @param {Object} data
29819          */
29820         "upload" : true,
29821         /**
29822          * @event arrange
29823          * Fire when arrange the file data
29824          * @param {Roo.bootstrap.UploadCropbox} this
29825          * @param {Object} formData
29826          */
29827         "arrange" : true
29828     });
29829     
29830     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29831 };
29832
29833 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29834     
29835     emptyText : 'Click to upload image',
29836     rotateNotify : 'Image is too small to rotate',
29837     errorTimeout : 3000,
29838     scale : 0,
29839     baseScale : 1,
29840     rotate : 0,
29841     dragable : false,
29842     pinching : false,
29843     mouseX : 0,
29844     mouseY : 0,
29845     cropData : false,
29846     minWidth : 300,
29847     minHeight : 300,
29848     file : false,
29849     exif : {},
29850     baseRotate : 1,
29851     cropType : 'image/jpeg',
29852     buttons : false,
29853     canvasLoaded : false,
29854     isDocument : false,
29855     method : 'POST',
29856     paramName : 'imageUpload',
29857     loadMask : true,
29858     loadingText : 'Loading...',
29859     maskEl : false,
29860     
29861     getAutoCreate : function()
29862     {
29863         var cfg = {
29864             tag : 'div',
29865             cls : 'roo-upload-cropbox',
29866             cn : [
29867                 {
29868                     tag : 'input',
29869                     cls : 'roo-upload-cropbox-selector',
29870                     type : 'file'
29871                 },
29872                 {
29873                     tag : 'div',
29874                     cls : 'roo-upload-cropbox-body',
29875                     style : 'cursor:pointer',
29876                     cn : [
29877                         {
29878                             tag : 'div',
29879                             cls : 'roo-upload-cropbox-preview'
29880                         },
29881                         {
29882                             tag : 'div',
29883                             cls : 'roo-upload-cropbox-thumb'
29884                         },
29885                         {
29886                             tag : 'div',
29887                             cls : 'roo-upload-cropbox-empty-notify',
29888                             html : this.emptyText
29889                         },
29890                         {
29891                             tag : 'div',
29892                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29893                             html : this.rotateNotify
29894                         }
29895                     ]
29896                 },
29897                 {
29898                     tag : 'div',
29899                     cls : 'roo-upload-cropbox-footer',
29900                     cn : {
29901                         tag : 'div',
29902                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29903                         cn : []
29904                     }
29905                 }
29906             ]
29907         };
29908         
29909         return cfg;
29910     },
29911     
29912     onRender : function(ct, position)
29913     {
29914         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29915         
29916         if (this.buttons.length) {
29917             
29918             Roo.each(this.buttons, function(bb) {
29919                 
29920                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29921                 
29922                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29923                 
29924             }, this);
29925         }
29926         
29927         if(this.loadMask){
29928             this.maskEl = this.el;
29929         }
29930     },
29931     
29932     initEvents : function()
29933     {
29934         this.urlAPI = (window.createObjectURL && window) || 
29935                                 (window.URL && URL.revokeObjectURL && URL) || 
29936                                 (window.webkitURL && webkitURL);
29937                         
29938         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29939         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29940         
29941         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29942         this.selectorEl.hide();
29943         
29944         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29945         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29946         
29947         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29948         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29949         this.thumbEl.hide();
29950         
29951         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29952         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29953         
29954         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29955         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29956         this.errorEl.hide();
29957         
29958         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29959         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29960         this.footerEl.hide();
29961         
29962         this.setThumbBoxSize();
29963         
29964         this.bind();
29965         
29966         this.resize();
29967         
29968         this.fireEvent('initial', this);
29969     },
29970
29971     bind : function()
29972     {
29973         var _this = this;
29974         
29975         window.addEventListener("resize", function() { _this.resize(); } );
29976         
29977         this.bodyEl.on('click', this.beforeSelectFile, this);
29978         
29979         if(Roo.isTouch){
29980             this.bodyEl.on('touchstart', this.onTouchStart, this);
29981             this.bodyEl.on('touchmove', this.onTouchMove, this);
29982             this.bodyEl.on('touchend', this.onTouchEnd, this);
29983         }
29984         
29985         if(!Roo.isTouch){
29986             this.bodyEl.on('mousedown', this.onMouseDown, this);
29987             this.bodyEl.on('mousemove', this.onMouseMove, this);
29988             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29989             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29990             Roo.get(document).on('mouseup', this.onMouseUp, this);
29991         }
29992         
29993         this.selectorEl.on('change', this.onFileSelected, this);
29994     },
29995     
29996     reset : function()
29997     {    
29998         this.scale = 0;
29999         this.baseScale = 1;
30000         this.rotate = 0;
30001         this.baseRotate = 1;
30002         this.dragable = false;
30003         this.pinching = false;
30004         this.mouseX = 0;
30005         this.mouseY = 0;
30006         this.cropData = false;
30007         this.notifyEl.dom.innerHTML = this.emptyText;
30008         
30009         this.selectorEl.dom.value = '';
30010         
30011     },
30012     
30013     resize : function()
30014     {
30015         if(this.fireEvent('resize', this) != false){
30016             this.setThumbBoxPosition();
30017             this.setCanvasPosition();
30018         }
30019     },
30020     
30021     onFooterButtonClick : function(e, el, o, type)
30022     {
30023         switch (type) {
30024             case 'rotate-left' :
30025                 this.onRotateLeft(e);
30026                 break;
30027             case 'rotate-right' :
30028                 this.onRotateRight(e);
30029                 break;
30030             case 'picture' :
30031                 this.beforeSelectFile(e);
30032                 break;
30033             case 'trash' :
30034                 this.trash(e);
30035                 break;
30036             case 'crop' :
30037                 this.crop(e);
30038                 break;
30039             case 'download' :
30040                 this.download(e);
30041                 break;
30042             default :
30043                 break;
30044         }
30045         
30046         this.fireEvent('footerbuttonclick', this, type);
30047     },
30048     
30049     beforeSelectFile : function(e)
30050     {
30051         e.preventDefault();
30052         
30053         if(this.fireEvent('beforeselectfile', this) != false){
30054             this.selectorEl.dom.click();
30055         }
30056     },
30057     
30058     onFileSelected : function(e)
30059     {
30060         e.preventDefault();
30061         
30062         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30063             return;
30064         }
30065         
30066         var file = this.selectorEl.dom.files[0];
30067         
30068         if(this.fireEvent('inspect', this, file) != false){
30069             this.prepare(file);
30070         }
30071         
30072     },
30073     
30074     trash : function(e)
30075     {
30076         this.fireEvent('trash', this);
30077     },
30078     
30079     download : function(e)
30080     {
30081         this.fireEvent('download', this);
30082     },
30083     
30084     loadCanvas : function(src)
30085     {   
30086         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30087             
30088             this.reset();
30089             
30090             this.imageEl = document.createElement('img');
30091             
30092             var _this = this;
30093             
30094             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30095             
30096             this.imageEl.src = src;
30097         }
30098     },
30099     
30100     onLoadCanvas : function()
30101     {   
30102         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30103         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30104         
30105         this.bodyEl.un('click', this.beforeSelectFile, this);
30106         
30107         this.notifyEl.hide();
30108         this.thumbEl.show();
30109         this.footerEl.show();
30110         
30111         this.baseRotateLevel();
30112         
30113         if(this.isDocument){
30114             this.setThumbBoxSize();
30115         }
30116         
30117         this.setThumbBoxPosition();
30118         
30119         this.baseScaleLevel();
30120         
30121         this.draw();
30122         
30123         this.resize();
30124         
30125         this.canvasLoaded = true;
30126         
30127         if(this.loadMask){
30128             this.maskEl.unmask();
30129         }
30130         
30131     },
30132     
30133     setCanvasPosition : function()
30134     {   
30135         if(!this.canvasEl){
30136             return;
30137         }
30138         
30139         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30140         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30141         
30142         this.previewEl.setLeft(pw);
30143         this.previewEl.setTop(ph);
30144         
30145     },
30146     
30147     onMouseDown : function(e)
30148     {   
30149         e.stopEvent();
30150         
30151         this.dragable = true;
30152         this.pinching = false;
30153         
30154         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30155             this.dragable = false;
30156             return;
30157         }
30158         
30159         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30160         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30161         
30162     },
30163     
30164     onMouseMove : function(e)
30165     {   
30166         e.stopEvent();
30167         
30168         if(!this.canvasLoaded){
30169             return;
30170         }
30171         
30172         if (!this.dragable){
30173             return;
30174         }
30175         
30176         var minX = Math.ceil(this.thumbEl.getLeft(true));
30177         var minY = Math.ceil(this.thumbEl.getTop(true));
30178         
30179         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30180         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30181         
30182         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30183         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30184         
30185         x = x - this.mouseX;
30186         y = y - this.mouseY;
30187         
30188         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30189         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30190         
30191         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30192         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30193         
30194         this.previewEl.setLeft(bgX);
30195         this.previewEl.setTop(bgY);
30196         
30197         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30198         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30199     },
30200     
30201     onMouseUp : function(e)
30202     {   
30203         e.stopEvent();
30204         
30205         this.dragable = false;
30206     },
30207     
30208     onMouseWheel : function(e)
30209     {   
30210         e.stopEvent();
30211         
30212         this.startScale = this.scale;
30213         
30214         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30215         
30216         if(!this.zoomable()){
30217             this.scale = this.startScale;
30218             return;
30219         }
30220         
30221         this.draw();
30222         
30223         return;
30224     },
30225     
30226     zoomable : function()
30227     {
30228         var minScale = this.thumbEl.getWidth() / this.minWidth;
30229         
30230         if(this.minWidth < this.minHeight){
30231             minScale = this.thumbEl.getHeight() / this.minHeight;
30232         }
30233         
30234         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30235         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30236         
30237         if(
30238                 this.isDocument &&
30239                 (this.rotate == 0 || this.rotate == 180) && 
30240                 (
30241                     width > this.imageEl.OriginWidth || 
30242                     height > this.imageEl.OriginHeight ||
30243                     (width < this.minWidth && height < this.minHeight)
30244                 )
30245         ){
30246             return false;
30247         }
30248         
30249         if(
30250                 this.isDocument &&
30251                 (this.rotate == 90 || this.rotate == 270) && 
30252                 (
30253                     width > this.imageEl.OriginWidth || 
30254                     height > this.imageEl.OriginHeight ||
30255                     (width < this.minHeight && height < this.minWidth)
30256                 )
30257         ){
30258             return false;
30259         }
30260         
30261         if(
30262                 !this.isDocument &&
30263                 (this.rotate == 0 || this.rotate == 180) && 
30264                 (
30265                     width < this.minWidth || 
30266                     width > this.imageEl.OriginWidth || 
30267                     height < this.minHeight || 
30268                     height > this.imageEl.OriginHeight
30269                 )
30270         ){
30271             return false;
30272         }
30273         
30274         if(
30275                 !this.isDocument &&
30276                 (this.rotate == 90 || this.rotate == 270) && 
30277                 (
30278                     width < this.minHeight || 
30279                     width > this.imageEl.OriginWidth || 
30280                     height < this.minWidth || 
30281                     height > this.imageEl.OriginHeight
30282                 )
30283         ){
30284             return false;
30285         }
30286         
30287         return true;
30288         
30289     },
30290     
30291     onRotateLeft : function(e)
30292     {   
30293         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30294             
30295             var minScale = this.thumbEl.getWidth() / this.minWidth;
30296             
30297             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30298             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30299             
30300             this.startScale = this.scale;
30301             
30302             while (this.getScaleLevel() < minScale){
30303             
30304                 this.scale = this.scale + 1;
30305                 
30306                 if(!this.zoomable()){
30307                     break;
30308                 }
30309                 
30310                 if(
30311                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30312                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30313                 ){
30314                     continue;
30315                 }
30316                 
30317                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30318
30319                 this.draw();
30320                 
30321                 return;
30322             }
30323             
30324             this.scale = this.startScale;
30325             
30326             this.onRotateFail();
30327             
30328             return false;
30329         }
30330         
30331         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30332
30333         if(this.isDocument){
30334             this.setThumbBoxSize();
30335             this.setThumbBoxPosition();
30336             this.setCanvasPosition();
30337         }
30338         
30339         this.draw();
30340         
30341         this.fireEvent('rotate', this, 'left');
30342         
30343     },
30344     
30345     onRotateRight : function(e)
30346     {
30347         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30348             
30349             var minScale = this.thumbEl.getWidth() / this.minWidth;
30350         
30351             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30352             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30353             
30354             this.startScale = this.scale;
30355             
30356             while (this.getScaleLevel() < minScale){
30357             
30358                 this.scale = this.scale + 1;
30359                 
30360                 if(!this.zoomable()){
30361                     break;
30362                 }
30363                 
30364                 if(
30365                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30366                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30367                 ){
30368                     continue;
30369                 }
30370                 
30371                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30372
30373                 this.draw();
30374                 
30375                 return;
30376             }
30377             
30378             this.scale = this.startScale;
30379             
30380             this.onRotateFail();
30381             
30382             return false;
30383         }
30384         
30385         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30386
30387         if(this.isDocument){
30388             this.setThumbBoxSize();
30389             this.setThumbBoxPosition();
30390             this.setCanvasPosition();
30391         }
30392         
30393         this.draw();
30394         
30395         this.fireEvent('rotate', this, 'right');
30396     },
30397     
30398     onRotateFail : function()
30399     {
30400         this.errorEl.show(true);
30401         
30402         var _this = this;
30403         
30404         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30405     },
30406     
30407     draw : function()
30408     {
30409         this.previewEl.dom.innerHTML = '';
30410         
30411         var canvasEl = document.createElement("canvas");
30412         
30413         var contextEl = canvasEl.getContext("2d");
30414         
30415         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30416         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30417         var center = this.imageEl.OriginWidth / 2;
30418         
30419         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30420             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30421             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30422             center = this.imageEl.OriginHeight / 2;
30423         }
30424         
30425         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30426         
30427         contextEl.translate(center, center);
30428         contextEl.rotate(this.rotate * Math.PI / 180);
30429
30430         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30431         
30432         this.canvasEl = document.createElement("canvas");
30433         
30434         this.contextEl = this.canvasEl.getContext("2d");
30435         
30436         switch (this.rotate) {
30437             case 0 :
30438                 
30439                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30440                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30441                 
30442                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30443                 
30444                 break;
30445             case 90 : 
30446                 
30447                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30448                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30449                 
30450                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30451                     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);
30452                     break;
30453                 }
30454                 
30455                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30456                 
30457                 break;
30458             case 180 :
30459                 
30460                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30461                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30462                 
30463                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30464                     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);
30465                     break;
30466                 }
30467                 
30468                 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);
30469                 
30470                 break;
30471             case 270 :
30472                 
30473                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30474                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30475         
30476                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30477                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30478                     break;
30479                 }
30480                 
30481                 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);
30482                 
30483                 break;
30484             default : 
30485                 break;
30486         }
30487         
30488         this.previewEl.appendChild(this.canvasEl);
30489         
30490         this.setCanvasPosition();
30491     },
30492     
30493     crop : function()
30494     {
30495         if(!this.canvasLoaded){
30496             return;
30497         }
30498         
30499         var imageCanvas = document.createElement("canvas");
30500         
30501         var imageContext = imageCanvas.getContext("2d");
30502         
30503         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30504         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30505         
30506         var center = imageCanvas.width / 2;
30507         
30508         imageContext.translate(center, center);
30509         
30510         imageContext.rotate(this.rotate * Math.PI / 180);
30511         
30512         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30513         
30514         var canvas = document.createElement("canvas");
30515         
30516         var context = canvas.getContext("2d");
30517                 
30518         canvas.width = this.minWidth;
30519         canvas.height = this.minHeight;
30520
30521         switch (this.rotate) {
30522             case 0 :
30523                 
30524                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30525                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30526                 
30527                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30528                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30529                 
30530                 var targetWidth = this.minWidth - 2 * x;
30531                 var targetHeight = this.minHeight - 2 * y;
30532                 
30533                 var scale = 1;
30534                 
30535                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30536                     scale = targetWidth / width;
30537                 }
30538                 
30539                 if(x > 0 && y == 0){
30540                     scale = targetHeight / height;
30541                 }
30542                 
30543                 if(x > 0 && y > 0){
30544                     scale = targetWidth / width;
30545                     
30546                     if(width < height){
30547                         scale = targetHeight / height;
30548                     }
30549                 }
30550                 
30551                 context.scale(scale, scale);
30552                 
30553                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30554                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30555
30556                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30557                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30558
30559                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30560                 
30561                 break;
30562             case 90 : 
30563                 
30564                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30565                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30566                 
30567                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30568                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30569                 
30570                 var targetWidth = this.minWidth - 2 * x;
30571                 var targetHeight = this.minHeight - 2 * y;
30572                 
30573                 var scale = 1;
30574                 
30575                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30576                     scale = targetWidth / width;
30577                 }
30578                 
30579                 if(x > 0 && y == 0){
30580                     scale = targetHeight / height;
30581                 }
30582                 
30583                 if(x > 0 && y > 0){
30584                     scale = targetWidth / width;
30585                     
30586                     if(width < height){
30587                         scale = targetHeight / height;
30588                     }
30589                 }
30590                 
30591                 context.scale(scale, scale);
30592                 
30593                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30594                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30595
30596                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30597                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30598                 
30599                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30600                 
30601                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30602                 
30603                 break;
30604             case 180 :
30605                 
30606                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30607                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30608                 
30609                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30610                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30611                 
30612                 var targetWidth = this.minWidth - 2 * x;
30613                 var targetHeight = this.minHeight - 2 * y;
30614                 
30615                 var scale = 1;
30616                 
30617                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30618                     scale = targetWidth / width;
30619                 }
30620                 
30621                 if(x > 0 && y == 0){
30622                     scale = targetHeight / height;
30623                 }
30624                 
30625                 if(x > 0 && y > 0){
30626                     scale = targetWidth / width;
30627                     
30628                     if(width < height){
30629                         scale = targetHeight / height;
30630                     }
30631                 }
30632                 
30633                 context.scale(scale, scale);
30634                 
30635                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30636                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30637
30638                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30639                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30640
30641                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30642                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30643                 
30644                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30645                 
30646                 break;
30647             case 270 :
30648                 
30649                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30650                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30651                 
30652                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30653                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30654                 
30655                 var targetWidth = this.minWidth - 2 * x;
30656                 var targetHeight = this.minHeight - 2 * y;
30657                 
30658                 var scale = 1;
30659                 
30660                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30661                     scale = targetWidth / width;
30662                 }
30663                 
30664                 if(x > 0 && y == 0){
30665                     scale = targetHeight / height;
30666                 }
30667                 
30668                 if(x > 0 && y > 0){
30669                     scale = targetWidth / width;
30670                     
30671                     if(width < height){
30672                         scale = targetHeight / height;
30673                     }
30674                 }
30675                 
30676                 context.scale(scale, scale);
30677                 
30678                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30679                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30680
30681                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30682                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30683                 
30684                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30685                 
30686                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30687                 
30688                 break;
30689             default : 
30690                 break;
30691         }
30692         
30693         this.cropData = canvas.toDataURL(this.cropType);
30694         
30695         if(this.fireEvent('crop', this, this.cropData) !== false){
30696             this.process(this.file, this.cropData);
30697         }
30698         
30699         return;
30700         
30701     },
30702     
30703     setThumbBoxSize : function()
30704     {
30705         var width, height;
30706         
30707         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30708             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30709             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30710             
30711             this.minWidth = width;
30712             this.minHeight = height;
30713             
30714             if(this.rotate == 90 || this.rotate == 270){
30715                 this.minWidth = height;
30716                 this.minHeight = width;
30717             }
30718         }
30719         
30720         height = 300;
30721         width = Math.ceil(this.minWidth * height / this.minHeight);
30722         
30723         if(this.minWidth > this.minHeight){
30724             width = 300;
30725             height = Math.ceil(this.minHeight * width / this.minWidth);
30726         }
30727         
30728         this.thumbEl.setStyle({
30729             width : width + 'px',
30730             height : height + 'px'
30731         });
30732
30733         return;
30734             
30735     },
30736     
30737     setThumbBoxPosition : function()
30738     {
30739         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30740         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30741         
30742         this.thumbEl.setLeft(x);
30743         this.thumbEl.setTop(y);
30744         
30745     },
30746     
30747     baseRotateLevel : function()
30748     {
30749         this.baseRotate = 1;
30750         
30751         if(
30752                 typeof(this.exif) != 'undefined' &&
30753                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30754                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30755         ){
30756             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30757         }
30758         
30759         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30760         
30761     },
30762     
30763     baseScaleLevel : function()
30764     {
30765         var width, height;
30766         
30767         if(this.isDocument){
30768             
30769             if(this.baseRotate == 6 || this.baseRotate == 8){
30770             
30771                 height = this.thumbEl.getHeight();
30772                 this.baseScale = height / this.imageEl.OriginWidth;
30773
30774                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30775                     width = this.thumbEl.getWidth();
30776                     this.baseScale = width / this.imageEl.OriginHeight;
30777                 }
30778
30779                 return;
30780             }
30781
30782             height = this.thumbEl.getHeight();
30783             this.baseScale = height / this.imageEl.OriginHeight;
30784
30785             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30786                 width = this.thumbEl.getWidth();
30787                 this.baseScale = width / this.imageEl.OriginWidth;
30788             }
30789
30790             return;
30791         }
30792         
30793         if(this.baseRotate == 6 || this.baseRotate == 8){
30794             
30795             width = this.thumbEl.getHeight();
30796             this.baseScale = width / this.imageEl.OriginHeight;
30797             
30798             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30799                 height = this.thumbEl.getWidth();
30800                 this.baseScale = height / this.imageEl.OriginHeight;
30801             }
30802             
30803             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30804                 height = this.thumbEl.getWidth();
30805                 this.baseScale = height / this.imageEl.OriginHeight;
30806                 
30807                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30808                     width = this.thumbEl.getHeight();
30809                     this.baseScale = width / this.imageEl.OriginWidth;
30810                 }
30811             }
30812             
30813             return;
30814         }
30815         
30816         width = this.thumbEl.getWidth();
30817         this.baseScale = width / this.imageEl.OriginWidth;
30818         
30819         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30820             height = this.thumbEl.getHeight();
30821             this.baseScale = height / this.imageEl.OriginHeight;
30822         }
30823         
30824         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30825             
30826             height = this.thumbEl.getHeight();
30827             this.baseScale = height / this.imageEl.OriginHeight;
30828             
30829             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30830                 width = this.thumbEl.getWidth();
30831                 this.baseScale = width / this.imageEl.OriginWidth;
30832             }
30833             
30834         }
30835         
30836         return;
30837     },
30838     
30839     getScaleLevel : function()
30840     {
30841         return this.baseScale * Math.pow(1.1, this.scale);
30842     },
30843     
30844     onTouchStart : function(e)
30845     {
30846         if(!this.canvasLoaded){
30847             this.beforeSelectFile(e);
30848             return;
30849         }
30850         
30851         var touches = e.browserEvent.touches;
30852         
30853         if(!touches){
30854             return;
30855         }
30856         
30857         if(touches.length == 1){
30858             this.onMouseDown(e);
30859             return;
30860         }
30861         
30862         if(touches.length != 2){
30863             return;
30864         }
30865         
30866         var coords = [];
30867         
30868         for(var i = 0, finger; finger = touches[i]; i++){
30869             coords.push(finger.pageX, finger.pageY);
30870         }
30871         
30872         var x = Math.pow(coords[0] - coords[2], 2);
30873         var y = Math.pow(coords[1] - coords[3], 2);
30874         
30875         this.startDistance = Math.sqrt(x + y);
30876         
30877         this.startScale = this.scale;
30878         
30879         this.pinching = true;
30880         this.dragable = false;
30881         
30882     },
30883     
30884     onTouchMove : function(e)
30885     {
30886         if(!this.pinching && !this.dragable){
30887             return;
30888         }
30889         
30890         var touches = e.browserEvent.touches;
30891         
30892         if(!touches){
30893             return;
30894         }
30895         
30896         if(this.dragable){
30897             this.onMouseMove(e);
30898             return;
30899         }
30900         
30901         var coords = [];
30902         
30903         for(var i = 0, finger; finger = touches[i]; i++){
30904             coords.push(finger.pageX, finger.pageY);
30905         }
30906         
30907         var x = Math.pow(coords[0] - coords[2], 2);
30908         var y = Math.pow(coords[1] - coords[3], 2);
30909         
30910         this.endDistance = Math.sqrt(x + y);
30911         
30912         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30913         
30914         if(!this.zoomable()){
30915             this.scale = this.startScale;
30916             return;
30917         }
30918         
30919         this.draw();
30920         
30921     },
30922     
30923     onTouchEnd : function(e)
30924     {
30925         this.pinching = false;
30926         this.dragable = false;
30927         
30928     },
30929     
30930     process : function(file, crop)
30931     {
30932         if(this.loadMask){
30933             this.maskEl.mask(this.loadingText);
30934         }
30935         
30936         this.xhr = new XMLHttpRequest();
30937         
30938         file.xhr = this.xhr;
30939
30940         this.xhr.open(this.method, this.url, true);
30941         
30942         var headers = {
30943             "Accept": "application/json",
30944             "Cache-Control": "no-cache",
30945             "X-Requested-With": "XMLHttpRequest"
30946         };
30947         
30948         for (var headerName in headers) {
30949             var headerValue = headers[headerName];
30950             if (headerValue) {
30951                 this.xhr.setRequestHeader(headerName, headerValue);
30952             }
30953         }
30954         
30955         var _this = this;
30956         
30957         this.xhr.onload = function()
30958         {
30959             _this.xhrOnLoad(_this.xhr);
30960         }
30961         
30962         this.xhr.onerror = function()
30963         {
30964             _this.xhrOnError(_this.xhr);
30965         }
30966         
30967         var formData = new FormData();
30968
30969         formData.append('returnHTML', 'NO');
30970         
30971         if(crop){
30972             formData.append('crop', crop);
30973         }
30974         
30975         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30976             formData.append(this.paramName, file, file.name);
30977         }
30978         
30979         if(typeof(file.filename) != 'undefined'){
30980             formData.append('filename', file.filename);
30981         }
30982         
30983         if(typeof(file.mimetype) != 'undefined'){
30984             formData.append('mimetype', file.mimetype);
30985         }
30986         
30987         if(this.fireEvent('arrange', this, formData) != false){
30988             this.xhr.send(formData);
30989         };
30990     },
30991     
30992     xhrOnLoad : function(xhr)
30993     {
30994         if(this.loadMask){
30995             this.maskEl.unmask();
30996         }
30997         
30998         if (xhr.readyState !== 4) {
30999             this.fireEvent('exception', this, xhr);
31000             return;
31001         }
31002
31003         var response = Roo.decode(xhr.responseText);
31004         
31005         if(!response.success){
31006             this.fireEvent('exception', this, xhr);
31007             return;
31008         }
31009         
31010         var response = Roo.decode(xhr.responseText);
31011         
31012         this.fireEvent('upload', this, response);
31013         
31014     },
31015     
31016     xhrOnError : function()
31017     {
31018         if(this.loadMask){
31019             this.maskEl.unmask();
31020         }
31021         
31022         Roo.log('xhr on error');
31023         
31024         var response = Roo.decode(xhr.responseText);
31025           
31026         Roo.log(response);
31027         
31028     },
31029     
31030     prepare : function(file)
31031     {   
31032         if(this.loadMask){
31033             this.maskEl.mask(this.loadingText);
31034         }
31035         
31036         this.file = false;
31037         this.exif = {};
31038         
31039         if(typeof(file) === 'string'){
31040             this.loadCanvas(file);
31041             return;
31042         }
31043         
31044         if(!file || !this.urlAPI){
31045             return;
31046         }
31047         
31048         this.file = file;
31049         this.cropType = file.type;
31050         
31051         var _this = this;
31052         
31053         if(this.fireEvent('prepare', this, this.file) != false){
31054             
31055             var reader = new FileReader();
31056             
31057             reader.onload = function (e) {
31058                 if (e.target.error) {
31059                     Roo.log(e.target.error);
31060                     return;
31061                 }
31062                 
31063                 var buffer = e.target.result,
31064                     dataView = new DataView(buffer),
31065                     offset = 2,
31066                     maxOffset = dataView.byteLength - 4,
31067                     markerBytes,
31068                     markerLength;
31069                 
31070                 if (dataView.getUint16(0) === 0xffd8) {
31071                     while (offset < maxOffset) {
31072                         markerBytes = dataView.getUint16(offset);
31073                         
31074                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31075                             markerLength = dataView.getUint16(offset + 2) + 2;
31076                             if (offset + markerLength > dataView.byteLength) {
31077                                 Roo.log('Invalid meta data: Invalid segment size.');
31078                                 break;
31079                             }
31080                             
31081                             if(markerBytes == 0xffe1){
31082                                 _this.parseExifData(
31083                                     dataView,
31084                                     offset,
31085                                     markerLength
31086                                 );
31087                             }
31088                             
31089                             offset += markerLength;
31090                             
31091                             continue;
31092                         }
31093                         
31094                         break;
31095                     }
31096                     
31097                 }
31098                 
31099                 var url = _this.urlAPI.createObjectURL(_this.file);
31100                 
31101                 _this.loadCanvas(url);
31102                 
31103                 return;
31104             }
31105             
31106             reader.readAsArrayBuffer(this.file);
31107             
31108         }
31109         
31110     },
31111     
31112     parseExifData : function(dataView, offset, length)
31113     {
31114         var tiffOffset = offset + 10,
31115             littleEndian,
31116             dirOffset;
31117     
31118         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31119             // No Exif data, might be XMP data instead
31120             return;
31121         }
31122         
31123         // Check for the ASCII code for "Exif" (0x45786966):
31124         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31125             // No Exif data, might be XMP data instead
31126             return;
31127         }
31128         if (tiffOffset + 8 > dataView.byteLength) {
31129             Roo.log('Invalid Exif data: Invalid segment size.');
31130             return;
31131         }
31132         // Check for the two null bytes:
31133         if (dataView.getUint16(offset + 8) !== 0x0000) {
31134             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31135             return;
31136         }
31137         // Check the byte alignment:
31138         switch (dataView.getUint16(tiffOffset)) {
31139         case 0x4949:
31140             littleEndian = true;
31141             break;
31142         case 0x4D4D:
31143             littleEndian = false;
31144             break;
31145         default:
31146             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31147             return;
31148         }
31149         // Check for the TIFF tag marker (0x002A):
31150         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31151             Roo.log('Invalid Exif data: Missing TIFF marker.');
31152             return;
31153         }
31154         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31155         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31156         
31157         this.parseExifTags(
31158             dataView,
31159             tiffOffset,
31160             tiffOffset + dirOffset,
31161             littleEndian
31162         );
31163     },
31164     
31165     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31166     {
31167         var tagsNumber,
31168             dirEndOffset,
31169             i;
31170         if (dirOffset + 6 > dataView.byteLength) {
31171             Roo.log('Invalid Exif data: Invalid directory offset.');
31172             return;
31173         }
31174         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31175         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31176         if (dirEndOffset + 4 > dataView.byteLength) {
31177             Roo.log('Invalid Exif data: Invalid directory size.');
31178             return;
31179         }
31180         for (i = 0; i < tagsNumber; i += 1) {
31181             this.parseExifTag(
31182                 dataView,
31183                 tiffOffset,
31184                 dirOffset + 2 + 12 * i, // tag offset
31185                 littleEndian
31186             );
31187         }
31188         // Return the offset to the next directory:
31189         return dataView.getUint32(dirEndOffset, littleEndian);
31190     },
31191     
31192     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31193     {
31194         var tag = dataView.getUint16(offset, littleEndian);
31195         
31196         this.exif[tag] = this.getExifValue(
31197             dataView,
31198             tiffOffset,
31199             offset,
31200             dataView.getUint16(offset + 2, littleEndian), // tag type
31201             dataView.getUint32(offset + 4, littleEndian), // tag length
31202             littleEndian
31203         );
31204     },
31205     
31206     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31207     {
31208         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31209             tagSize,
31210             dataOffset,
31211             values,
31212             i,
31213             str,
31214             c;
31215     
31216         if (!tagType) {
31217             Roo.log('Invalid Exif data: Invalid tag type.');
31218             return;
31219         }
31220         
31221         tagSize = tagType.size * length;
31222         // Determine if the value is contained in the dataOffset bytes,
31223         // or if the value at the dataOffset is a pointer to the actual data:
31224         dataOffset = tagSize > 4 ?
31225                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31226         if (dataOffset + tagSize > dataView.byteLength) {
31227             Roo.log('Invalid Exif data: Invalid data offset.');
31228             return;
31229         }
31230         if (length === 1) {
31231             return tagType.getValue(dataView, dataOffset, littleEndian);
31232         }
31233         values = [];
31234         for (i = 0; i < length; i += 1) {
31235             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31236         }
31237         
31238         if (tagType.ascii) {
31239             str = '';
31240             // Concatenate the chars:
31241             for (i = 0; i < values.length; i += 1) {
31242                 c = values[i];
31243                 // Ignore the terminating NULL byte(s):
31244                 if (c === '\u0000') {
31245                     break;
31246                 }
31247                 str += c;
31248             }
31249             return str;
31250         }
31251         return values;
31252     }
31253     
31254 });
31255
31256 Roo.apply(Roo.bootstrap.UploadCropbox, {
31257     tags : {
31258         'Orientation': 0x0112
31259     },
31260     
31261     Orientation: {
31262             1: 0, //'top-left',
31263 //            2: 'top-right',
31264             3: 180, //'bottom-right',
31265 //            4: 'bottom-left',
31266 //            5: 'left-top',
31267             6: 90, //'right-top',
31268 //            7: 'right-bottom',
31269             8: 270 //'left-bottom'
31270     },
31271     
31272     exifTagTypes : {
31273         // byte, 8-bit unsigned int:
31274         1: {
31275             getValue: function (dataView, dataOffset) {
31276                 return dataView.getUint8(dataOffset);
31277             },
31278             size: 1
31279         },
31280         // ascii, 8-bit byte:
31281         2: {
31282             getValue: function (dataView, dataOffset) {
31283                 return String.fromCharCode(dataView.getUint8(dataOffset));
31284             },
31285             size: 1,
31286             ascii: true
31287         },
31288         // short, 16 bit int:
31289         3: {
31290             getValue: function (dataView, dataOffset, littleEndian) {
31291                 return dataView.getUint16(dataOffset, littleEndian);
31292             },
31293             size: 2
31294         },
31295         // long, 32 bit int:
31296         4: {
31297             getValue: function (dataView, dataOffset, littleEndian) {
31298                 return dataView.getUint32(dataOffset, littleEndian);
31299             },
31300             size: 4
31301         },
31302         // rational = two long values, first is numerator, second is denominator:
31303         5: {
31304             getValue: function (dataView, dataOffset, littleEndian) {
31305                 return dataView.getUint32(dataOffset, littleEndian) /
31306                     dataView.getUint32(dataOffset + 4, littleEndian);
31307             },
31308             size: 8
31309         },
31310         // slong, 32 bit signed int:
31311         9: {
31312             getValue: function (dataView, dataOffset, littleEndian) {
31313                 return dataView.getInt32(dataOffset, littleEndian);
31314             },
31315             size: 4
31316         },
31317         // srational, two slongs, first is numerator, second is denominator:
31318         10: {
31319             getValue: function (dataView, dataOffset, littleEndian) {
31320                 return dataView.getInt32(dataOffset, littleEndian) /
31321                     dataView.getInt32(dataOffset + 4, littleEndian);
31322             },
31323             size: 8
31324         }
31325     },
31326     
31327     footer : {
31328         STANDARD : [
31329             {
31330                 tag : 'div',
31331                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31332                 action : 'rotate-left',
31333                 cn : [
31334                     {
31335                         tag : 'button',
31336                         cls : 'btn btn-default',
31337                         html : '<i class="fa fa-undo"></i>'
31338                     }
31339                 ]
31340             },
31341             {
31342                 tag : 'div',
31343                 cls : 'btn-group roo-upload-cropbox-picture',
31344                 action : 'picture',
31345                 cn : [
31346                     {
31347                         tag : 'button',
31348                         cls : 'btn btn-default',
31349                         html : '<i class="fa fa-picture-o"></i>'
31350                     }
31351                 ]
31352             },
31353             {
31354                 tag : 'div',
31355                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31356                 action : 'rotate-right',
31357                 cn : [
31358                     {
31359                         tag : 'button',
31360                         cls : 'btn btn-default',
31361                         html : '<i class="fa fa-repeat"></i>'
31362                     }
31363                 ]
31364             }
31365         ],
31366         DOCUMENT : [
31367             {
31368                 tag : 'div',
31369                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31370                 action : 'rotate-left',
31371                 cn : [
31372                     {
31373                         tag : 'button',
31374                         cls : 'btn btn-default',
31375                         html : '<i class="fa fa-undo"></i>'
31376                     }
31377                 ]
31378             },
31379             {
31380                 tag : 'div',
31381                 cls : 'btn-group roo-upload-cropbox-download',
31382                 action : 'download',
31383                 cn : [
31384                     {
31385                         tag : 'button',
31386                         cls : 'btn btn-default',
31387                         html : '<i class="fa fa-download"></i>'
31388                     }
31389                 ]
31390             },
31391             {
31392                 tag : 'div',
31393                 cls : 'btn-group roo-upload-cropbox-crop',
31394                 action : 'crop',
31395                 cn : [
31396                     {
31397                         tag : 'button',
31398                         cls : 'btn btn-default',
31399                         html : '<i class="fa fa-crop"></i>'
31400                     }
31401                 ]
31402             },
31403             {
31404                 tag : 'div',
31405                 cls : 'btn-group roo-upload-cropbox-trash',
31406                 action : 'trash',
31407                 cn : [
31408                     {
31409                         tag : 'button',
31410                         cls : 'btn btn-default',
31411                         html : '<i class="fa fa-trash"></i>'
31412                     }
31413                 ]
31414             },
31415             {
31416                 tag : 'div',
31417                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31418                 action : 'rotate-right',
31419                 cn : [
31420                     {
31421                         tag : 'button',
31422                         cls : 'btn btn-default',
31423                         html : '<i class="fa fa-repeat"></i>'
31424                     }
31425                 ]
31426             }
31427         ],
31428         ROTATOR : [
31429             {
31430                 tag : 'div',
31431                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31432                 action : 'rotate-left',
31433                 cn : [
31434                     {
31435                         tag : 'button',
31436                         cls : 'btn btn-default',
31437                         html : '<i class="fa fa-undo"></i>'
31438                     }
31439                 ]
31440             },
31441             {
31442                 tag : 'div',
31443                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31444                 action : 'rotate-right',
31445                 cn : [
31446                     {
31447                         tag : 'button',
31448                         cls : 'btn btn-default',
31449                         html : '<i class="fa fa-repeat"></i>'
31450                     }
31451                 ]
31452             }
31453         ]
31454     }
31455 });
31456
31457 /*
31458 * Licence: LGPL
31459 */
31460
31461 /**
31462  * @class Roo.bootstrap.DocumentManager
31463  * @extends Roo.bootstrap.Component
31464  * Bootstrap DocumentManager class
31465  * @cfg {String} paramName default 'imageUpload'
31466  * @cfg {String} toolTipName default 'filename'
31467  * @cfg {String} method default POST
31468  * @cfg {String} url action url
31469  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31470  * @cfg {Boolean} multiple multiple upload default true
31471  * @cfg {Number} thumbSize default 300
31472  * @cfg {String} fieldLabel
31473  * @cfg {Number} labelWidth default 4
31474  * @cfg {String} labelAlign (left|top) default left
31475  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31476 * @cfg {Number} labellg set the width of label (1-12)
31477  * @cfg {Number} labelmd set the width of label (1-12)
31478  * @cfg {Number} labelsm set the width of label (1-12)
31479  * @cfg {Number} labelxs set the width of label (1-12)
31480  * 
31481  * @constructor
31482  * Create a new DocumentManager
31483  * @param {Object} config The config object
31484  */
31485
31486 Roo.bootstrap.DocumentManager = function(config){
31487     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31488     
31489     this.files = [];
31490     this.delegates = [];
31491     
31492     this.addEvents({
31493         /**
31494          * @event initial
31495          * Fire when initial the DocumentManager
31496          * @param {Roo.bootstrap.DocumentManager} this
31497          */
31498         "initial" : true,
31499         /**
31500          * @event inspect
31501          * inspect selected file
31502          * @param {Roo.bootstrap.DocumentManager} this
31503          * @param {File} file
31504          */
31505         "inspect" : true,
31506         /**
31507          * @event exception
31508          * Fire when xhr load exception
31509          * @param {Roo.bootstrap.DocumentManager} this
31510          * @param {XMLHttpRequest} xhr
31511          */
31512         "exception" : true,
31513         /**
31514          * @event afterupload
31515          * Fire when xhr load exception
31516          * @param {Roo.bootstrap.DocumentManager} this
31517          * @param {XMLHttpRequest} xhr
31518          */
31519         "afterupload" : true,
31520         /**
31521          * @event prepare
31522          * prepare the form data
31523          * @param {Roo.bootstrap.DocumentManager} this
31524          * @param {Object} formData
31525          */
31526         "prepare" : true,
31527         /**
31528          * @event remove
31529          * Fire when remove the file
31530          * @param {Roo.bootstrap.DocumentManager} this
31531          * @param {Object} file
31532          */
31533         "remove" : true,
31534         /**
31535          * @event refresh
31536          * Fire after refresh the file
31537          * @param {Roo.bootstrap.DocumentManager} this
31538          */
31539         "refresh" : true,
31540         /**
31541          * @event click
31542          * Fire after click the image
31543          * @param {Roo.bootstrap.DocumentManager} this
31544          * @param {Object} file
31545          */
31546         "click" : true,
31547         /**
31548          * @event edit
31549          * Fire when upload a image and editable set to true
31550          * @param {Roo.bootstrap.DocumentManager} this
31551          * @param {Object} file
31552          */
31553         "edit" : true,
31554         /**
31555          * @event beforeselectfile
31556          * Fire before select file
31557          * @param {Roo.bootstrap.DocumentManager} this
31558          */
31559         "beforeselectfile" : true,
31560         /**
31561          * @event process
31562          * Fire before process file
31563          * @param {Roo.bootstrap.DocumentManager} this
31564          * @param {Object} file
31565          */
31566         "process" : true,
31567         /**
31568          * @event previewrendered
31569          * Fire when preview rendered
31570          * @param {Roo.bootstrap.DocumentManager} this
31571          * @param {Object} file
31572          */
31573         "previewrendered" : true,
31574         /**
31575          */
31576         "previewResize" : true
31577         
31578     });
31579 };
31580
31581 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31582     
31583     boxes : 0,
31584     inputName : '',
31585     thumbSize : 300,
31586     multiple : true,
31587     files : false,
31588     method : 'POST',
31589     url : '',
31590     paramName : 'imageUpload',
31591     toolTipName : 'filename',
31592     fieldLabel : '',
31593     labelWidth : 4,
31594     labelAlign : 'left',
31595     editable : true,
31596     delegates : false,
31597     xhr : false, 
31598     
31599     labellg : 0,
31600     labelmd : 0,
31601     labelsm : 0,
31602     labelxs : 0,
31603     
31604     getAutoCreate : function()
31605     {   
31606         var managerWidget = {
31607             tag : 'div',
31608             cls : 'roo-document-manager',
31609             cn : [
31610                 {
31611                     tag : 'input',
31612                     cls : 'roo-document-manager-selector',
31613                     type : 'file'
31614                 },
31615                 {
31616                     tag : 'div',
31617                     cls : 'roo-document-manager-uploader',
31618                     cn : [
31619                         {
31620                             tag : 'div',
31621                             cls : 'roo-document-manager-upload-btn',
31622                             html : '<i class="fa fa-plus"></i>'
31623                         }
31624                     ]
31625                     
31626                 }
31627             ]
31628         };
31629         
31630         var content = [
31631             {
31632                 tag : 'div',
31633                 cls : 'column col-md-12',
31634                 cn : managerWidget
31635             }
31636         ];
31637         
31638         if(this.fieldLabel.length){
31639             
31640             content = [
31641                 {
31642                     tag : 'div',
31643                     cls : 'column col-md-12',
31644                     html : this.fieldLabel
31645                 },
31646                 {
31647                     tag : 'div',
31648                     cls : 'column col-md-12',
31649                     cn : managerWidget
31650                 }
31651             ];
31652
31653             if(this.labelAlign == 'left'){
31654                 content = [
31655                     {
31656                         tag : 'div',
31657                         cls : 'column',
31658                         html : this.fieldLabel
31659                     },
31660                     {
31661                         tag : 'div',
31662                         cls : 'column',
31663                         cn : managerWidget
31664                     }
31665                 ];
31666                 
31667                 if(this.labelWidth > 12){
31668                     content[0].style = "width: " + this.labelWidth + 'px';
31669                 }
31670
31671                 if(this.labelWidth < 13 && this.labelmd == 0){
31672                     this.labelmd = this.labelWidth;
31673                 }
31674
31675                 if(this.labellg > 0){
31676                     content[0].cls += ' col-lg-' + this.labellg;
31677                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31678                 }
31679
31680                 if(this.labelmd > 0){
31681                     content[0].cls += ' col-md-' + this.labelmd;
31682                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31683                 }
31684
31685                 if(this.labelsm > 0){
31686                     content[0].cls += ' col-sm-' + this.labelsm;
31687                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31688                 }
31689
31690                 if(this.labelxs > 0){
31691                     content[0].cls += ' col-xs-' + this.labelxs;
31692                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31693                 }
31694                 
31695             }
31696         }
31697         
31698         var cfg = {
31699             tag : 'div',
31700             cls : 'row clearfix',
31701             cn : content
31702         };
31703         
31704         return cfg;
31705         
31706     },
31707     
31708     initEvents : function()
31709     {
31710         this.managerEl = this.el.select('.roo-document-manager', true).first();
31711         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31712         
31713         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31714         this.selectorEl.hide();
31715         
31716         if(this.multiple){
31717             this.selectorEl.attr('multiple', 'multiple');
31718         }
31719         
31720         this.selectorEl.on('change', this.onFileSelected, this);
31721         
31722         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31723         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31724         
31725         this.uploader.on('click', this.onUploaderClick, this);
31726         
31727         this.renderProgressDialog();
31728         
31729         var _this = this;
31730         
31731         window.addEventListener("resize", function() { _this.refresh(); } );
31732         
31733         this.fireEvent('initial', this);
31734     },
31735     
31736     renderProgressDialog : function()
31737     {
31738         var _this = this;
31739         
31740         this.progressDialog = new Roo.bootstrap.Modal({
31741             cls : 'roo-document-manager-progress-dialog',
31742             allow_close : false,
31743             animate : false,
31744             title : '',
31745             buttons : [
31746                 {
31747                     name  :'cancel',
31748                     weight : 'danger',
31749                     html : 'Cancel'
31750                 }
31751             ], 
31752             listeners : { 
31753                 btnclick : function() {
31754                     _this.uploadCancel();
31755                     this.hide();
31756                 }
31757             }
31758         });
31759          
31760         this.progressDialog.render(Roo.get(document.body));
31761          
31762         this.progress = new Roo.bootstrap.Progress({
31763             cls : 'roo-document-manager-progress',
31764             active : true,
31765             striped : true
31766         });
31767         
31768         this.progress.render(this.progressDialog.getChildContainer());
31769         
31770         this.progressBar = new Roo.bootstrap.ProgressBar({
31771             cls : 'roo-document-manager-progress-bar',
31772             aria_valuenow : 0,
31773             aria_valuemin : 0,
31774             aria_valuemax : 12,
31775             panel : 'success'
31776         });
31777         
31778         this.progressBar.render(this.progress.getChildContainer());
31779     },
31780     
31781     onUploaderClick : function(e)
31782     {
31783         e.preventDefault();
31784      
31785         if(this.fireEvent('beforeselectfile', this) != false){
31786             this.selectorEl.dom.click();
31787         }
31788         
31789     },
31790     
31791     onFileSelected : function(e)
31792     {
31793         e.preventDefault();
31794         
31795         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31796             return;
31797         }
31798         
31799         Roo.each(this.selectorEl.dom.files, function(file){
31800             if(this.fireEvent('inspect', this, file) != false){
31801                 this.files.push(file);
31802             }
31803         }, this);
31804         
31805         this.queue();
31806         
31807     },
31808     
31809     queue : function()
31810     {
31811         this.selectorEl.dom.value = '';
31812         
31813         if(!this.files || !this.files.length){
31814             return;
31815         }
31816         
31817         if(this.boxes > 0 && this.files.length > this.boxes){
31818             this.files = this.files.slice(0, this.boxes);
31819         }
31820         
31821         this.uploader.show();
31822         
31823         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31824             this.uploader.hide();
31825         }
31826         
31827         var _this = this;
31828         
31829         var files = [];
31830         
31831         var docs = [];
31832         
31833         Roo.each(this.files, function(file){
31834             
31835             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31836                 var f = this.renderPreview(file);
31837                 files.push(f);
31838                 return;
31839             }
31840             
31841             if(file.type.indexOf('image') != -1){
31842                 this.delegates.push(
31843                     (function(){
31844                         _this.process(file);
31845                     }).createDelegate(this)
31846                 );
31847         
31848                 return;
31849             }
31850             
31851             docs.push(
31852                 (function(){
31853                     _this.process(file);
31854                 }).createDelegate(this)
31855             );
31856             
31857         }, this);
31858         
31859         this.files = files;
31860         
31861         this.delegates = this.delegates.concat(docs);
31862         
31863         if(!this.delegates.length){
31864             this.refresh();
31865             return;
31866         }
31867         
31868         this.progressBar.aria_valuemax = this.delegates.length;
31869         
31870         this.arrange();
31871         
31872         return;
31873     },
31874     
31875     arrange : function()
31876     {
31877         if(!this.delegates.length){
31878             this.progressDialog.hide();
31879             this.refresh();
31880             return;
31881         }
31882         
31883         var delegate = this.delegates.shift();
31884         
31885         this.progressDialog.show();
31886         
31887         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31888         
31889         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31890         
31891         delegate();
31892     },
31893     
31894     refresh : function()
31895     {
31896         this.uploader.show();
31897         
31898         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31899             this.uploader.hide();
31900         }
31901         
31902         Roo.isTouch ? this.closable(false) : this.closable(true);
31903         
31904         this.fireEvent('refresh', this);
31905     },
31906     
31907     onRemove : function(e, el, o)
31908     {
31909         e.preventDefault();
31910         
31911         this.fireEvent('remove', this, o);
31912         
31913     },
31914     
31915     remove : function(o)
31916     {
31917         var files = [];
31918         
31919         Roo.each(this.files, function(file){
31920             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31921                 files.push(file);
31922                 return;
31923             }
31924
31925             o.target.remove();
31926
31927         }, this);
31928         
31929         this.files = files;
31930         
31931         this.refresh();
31932     },
31933     
31934     clear : function()
31935     {
31936         Roo.each(this.files, function(file){
31937             if(!file.target){
31938                 return;
31939             }
31940             
31941             file.target.remove();
31942
31943         }, this);
31944         
31945         this.files = [];
31946         
31947         this.refresh();
31948     },
31949     
31950     onClick : function(e, el, o)
31951     {
31952         e.preventDefault();
31953         
31954         this.fireEvent('click', this, o);
31955         
31956     },
31957     
31958     closable : function(closable)
31959     {
31960         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31961             
31962             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31963             
31964             if(closable){
31965                 el.show();
31966                 return;
31967             }
31968             
31969             el.hide();
31970             
31971         }, this);
31972     },
31973     
31974     xhrOnLoad : function(xhr)
31975     {
31976         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31977             el.remove();
31978         }, this);
31979         
31980         if (xhr.readyState !== 4) {
31981             this.arrange();
31982             this.fireEvent('exception', this, xhr);
31983             return;
31984         }
31985
31986         var response = Roo.decode(xhr.responseText);
31987         
31988         if(!response.success){
31989             this.arrange();
31990             this.fireEvent('exception', this, xhr);
31991             return;
31992         }
31993         
31994         var file = this.renderPreview(response.data);
31995         
31996         this.files.push(file);
31997         
31998         this.arrange();
31999         
32000         this.fireEvent('afterupload', this, xhr);
32001         
32002     },
32003     
32004     xhrOnError : function(xhr)
32005     {
32006         Roo.log('xhr on error');
32007         
32008         var response = Roo.decode(xhr.responseText);
32009           
32010         Roo.log(response);
32011         
32012         this.arrange();
32013     },
32014     
32015     process : function(file)
32016     {
32017         if(this.fireEvent('process', this, file) !== false){
32018             if(this.editable && file.type.indexOf('image') != -1){
32019                 this.fireEvent('edit', this, file);
32020                 return;
32021             }
32022
32023             this.uploadStart(file, false);
32024
32025             return;
32026         }
32027         
32028     },
32029     
32030     uploadStart : function(file, crop)
32031     {
32032         this.xhr = new XMLHttpRequest();
32033         
32034         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32035             this.arrange();
32036             return;
32037         }
32038         
32039         file.xhr = this.xhr;
32040             
32041         this.managerEl.createChild({
32042             tag : 'div',
32043             cls : 'roo-document-manager-loading',
32044             cn : [
32045                 {
32046                     tag : 'div',
32047                     tooltip : file.name,
32048                     cls : 'roo-document-manager-thumb',
32049                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32050                 }
32051             ]
32052
32053         });
32054
32055         this.xhr.open(this.method, this.url, true);
32056         
32057         var headers = {
32058             "Accept": "application/json",
32059             "Cache-Control": "no-cache",
32060             "X-Requested-With": "XMLHttpRequest"
32061         };
32062         
32063         for (var headerName in headers) {
32064             var headerValue = headers[headerName];
32065             if (headerValue) {
32066                 this.xhr.setRequestHeader(headerName, headerValue);
32067             }
32068         }
32069         
32070         var _this = this;
32071         
32072         this.xhr.onload = function()
32073         {
32074             _this.xhrOnLoad(_this.xhr);
32075         }
32076         
32077         this.xhr.onerror = function()
32078         {
32079             _this.xhrOnError(_this.xhr);
32080         }
32081         
32082         var formData = new FormData();
32083
32084         formData.append('returnHTML', 'NO');
32085         
32086         if(crop){
32087             formData.append('crop', crop);
32088         }
32089         
32090         formData.append(this.paramName, file, file.name);
32091         
32092         var options = {
32093             file : file, 
32094             manually : false
32095         };
32096         
32097         if(this.fireEvent('prepare', this, formData, options) != false){
32098             
32099             if(options.manually){
32100                 return;
32101             }
32102             
32103             this.xhr.send(formData);
32104             return;
32105         };
32106         
32107         this.uploadCancel();
32108     },
32109     
32110     uploadCancel : function()
32111     {
32112         if (this.xhr) {
32113             this.xhr.abort();
32114         }
32115         
32116         this.delegates = [];
32117         
32118         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32119             el.remove();
32120         }, this);
32121         
32122         this.arrange();
32123     },
32124     
32125     renderPreview : function(file)
32126     {
32127         if(typeof(file.target) != 'undefined' && file.target){
32128             return file;
32129         }
32130         
32131         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32132         
32133         var previewEl = this.managerEl.createChild({
32134             tag : 'div',
32135             cls : 'roo-document-manager-preview',
32136             cn : [
32137                 {
32138                     tag : 'div',
32139                     tooltip : file[this.toolTipName],
32140                     cls : 'roo-document-manager-thumb',
32141                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32142                 },
32143                 {
32144                     tag : 'button',
32145                     cls : 'close',
32146                     html : '<i class="fa fa-times-circle"></i>'
32147                 }
32148             ]
32149         });
32150
32151         var close = previewEl.select('button.close', true).first();
32152
32153         close.on('click', this.onRemove, this, file);
32154
32155         file.target = previewEl;
32156
32157         var image = previewEl.select('img', true).first();
32158         
32159         var _this = this;
32160         
32161         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32162         
32163         image.on('click', this.onClick, this, file);
32164         
32165         this.fireEvent('previewrendered', this, file);
32166         
32167         return file;
32168         
32169     },
32170     
32171     onPreviewLoad : function(file, image)
32172     {
32173         if(typeof(file.target) == 'undefined' || !file.target){
32174             return;
32175         }
32176         
32177         var width = image.dom.naturalWidth || image.dom.width;
32178         var height = image.dom.naturalHeight || image.dom.height;
32179         
32180         if(!this.previewResize) {
32181             return;
32182         }
32183         
32184         if(width > height){
32185             file.target.addClass('wide');
32186             return;
32187         }
32188         
32189         file.target.addClass('tall');
32190         return;
32191         
32192     },
32193     
32194     uploadFromSource : function(file, crop)
32195     {
32196         this.xhr = new XMLHttpRequest();
32197         
32198         this.managerEl.createChild({
32199             tag : 'div',
32200             cls : 'roo-document-manager-loading',
32201             cn : [
32202                 {
32203                     tag : 'div',
32204                     tooltip : file.name,
32205                     cls : 'roo-document-manager-thumb',
32206                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32207                 }
32208             ]
32209
32210         });
32211
32212         this.xhr.open(this.method, this.url, true);
32213         
32214         var headers = {
32215             "Accept": "application/json",
32216             "Cache-Control": "no-cache",
32217             "X-Requested-With": "XMLHttpRequest"
32218         };
32219         
32220         for (var headerName in headers) {
32221             var headerValue = headers[headerName];
32222             if (headerValue) {
32223                 this.xhr.setRequestHeader(headerName, headerValue);
32224             }
32225         }
32226         
32227         var _this = this;
32228         
32229         this.xhr.onload = function()
32230         {
32231             _this.xhrOnLoad(_this.xhr);
32232         }
32233         
32234         this.xhr.onerror = function()
32235         {
32236             _this.xhrOnError(_this.xhr);
32237         }
32238         
32239         var formData = new FormData();
32240
32241         formData.append('returnHTML', 'NO');
32242         
32243         formData.append('crop', crop);
32244         
32245         if(typeof(file.filename) != 'undefined'){
32246             formData.append('filename', file.filename);
32247         }
32248         
32249         if(typeof(file.mimetype) != 'undefined'){
32250             formData.append('mimetype', file.mimetype);
32251         }
32252         
32253         Roo.log(formData);
32254         
32255         if(this.fireEvent('prepare', this, formData) != false){
32256             this.xhr.send(formData);
32257         };
32258     }
32259 });
32260
32261 /*
32262 * Licence: LGPL
32263 */
32264
32265 /**
32266  * @class Roo.bootstrap.DocumentViewer
32267  * @extends Roo.bootstrap.Component
32268  * Bootstrap DocumentViewer class
32269  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32270  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32271  * 
32272  * @constructor
32273  * Create a new DocumentViewer
32274  * @param {Object} config The config object
32275  */
32276
32277 Roo.bootstrap.DocumentViewer = function(config){
32278     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32279     
32280     this.addEvents({
32281         /**
32282          * @event initial
32283          * Fire after initEvent
32284          * @param {Roo.bootstrap.DocumentViewer} this
32285          */
32286         "initial" : true,
32287         /**
32288          * @event click
32289          * Fire after click
32290          * @param {Roo.bootstrap.DocumentViewer} this
32291          */
32292         "click" : true,
32293         /**
32294          * @event download
32295          * Fire after download button
32296          * @param {Roo.bootstrap.DocumentViewer} this
32297          */
32298         "download" : true,
32299         /**
32300          * @event trash
32301          * Fire after trash button
32302          * @param {Roo.bootstrap.DocumentViewer} this
32303          */
32304         "trash" : true
32305         
32306     });
32307 };
32308
32309 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32310     
32311     showDownload : true,
32312     
32313     showTrash : true,
32314     
32315     getAutoCreate : function()
32316     {
32317         var cfg = {
32318             tag : 'div',
32319             cls : 'roo-document-viewer',
32320             cn : [
32321                 {
32322                     tag : 'div',
32323                     cls : 'roo-document-viewer-body',
32324                     cn : [
32325                         {
32326                             tag : 'div',
32327                             cls : 'roo-document-viewer-thumb',
32328                             cn : [
32329                                 {
32330                                     tag : 'img',
32331                                     cls : 'roo-document-viewer-image'
32332                                 }
32333                             ]
32334                         }
32335                     ]
32336                 },
32337                 {
32338                     tag : 'div',
32339                     cls : 'roo-document-viewer-footer',
32340                     cn : {
32341                         tag : 'div',
32342                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32343                         cn : [
32344                             {
32345                                 tag : 'div',
32346                                 cls : 'btn-group roo-document-viewer-download',
32347                                 cn : [
32348                                     {
32349                                         tag : 'button',
32350                                         cls : 'btn btn-default',
32351                                         html : '<i class="fa fa-download"></i>'
32352                                     }
32353                                 ]
32354                             },
32355                             {
32356                                 tag : 'div',
32357                                 cls : 'btn-group roo-document-viewer-trash',
32358                                 cn : [
32359                                     {
32360                                         tag : 'button',
32361                                         cls : 'btn btn-default',
32362                                         html : '<i class="fa fa-trash"></i>'
32363                                     }
32364                                 ]
32365                             }
32366                         ]
32367                     }
32368                 }
32369             ]
32370         };
32371         
32372         return cfg;
32373     },
32374     
32375     initEvents : function()
32376     {
32377         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32378         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32379         
32380         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32381         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32382         
32383         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32384         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32385         
32386         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32387         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32388         
32389         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32390         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32391         
32392         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32393         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32394         
32395         this.bodyEl.on('click', this.onClick, this);
32396         this.downloadBtn.on('click', this.onDownload, this);
32397         this.trashBtn.on('click', this.onTrash, this);
32398         
32399         this.downloadBtn.hide();
32400         this.trashBtn.hide();
32401         
32402         if(this.showDownload){
32403             this.downloadBtn.show();
32404         }
32405         
32406         if(this.showTrash){
32407             this.trashBtn.show();
32408         }
32409         
32410         if(!this.showDownload && !this.showTrash) {
32411             this.footerEl.hide();
32412         }
32413         
32414     },
32415     
32416     initial : function()
32417     {
32418         this.fireEvent('initial', this);
32419         
32420     },
32421     
32422     onClick : function(e)
32423     {
32424         e.preventDefault();
32425         
32426         this.fireEvent('click', this);
32427     },
32428     
32429     onDownload : function(e)
32430     {
32431         e.preventDefault();
32432         
32433         this.fireEvent('download', this);
32434     },
32435     
32436     onTrash : function(e)
32437     {
32438         e.preventDefault();
32439         
32440         this.fireEvent('trash', this);
32441     }
32442     
32443 });
32444 /*
32445  * - LGPL
32446  *
32447  * nav progress bar
32448  * 
32449  */
32450
32451 /**
32452  * @class Roo.bootstrap.NavProgressBar
32453  * @extends Roo.bootstrap.Component
32454  * Bootstrap NavProgressBar class
32455  * 
32456  * @constructor
32457  * Create a new nav progress bar
32458  * @param {Object} config The config object
32459  */
32460
32461 Roo.bootstrap.NavProgressBar = function(config){
32462     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32463
32464     this.bullets = this.bullets || [];
32465    
32466 //    Roo.bootstrap.NavProgressBar.register(this);
32467      this.addEvents({
32468         /**
32469              * @event changed
32470              * Fires when the active item changes
32471              * @param {Roo.bootstrap.NavProgressBar} this
32472              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32473              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32474          */
32475         'changed': true
32476      });
32477     
32478 };
32479
32480 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32481     
32482     bullets : [],
32483     barItems : [],
32484     
32485     getAutoCreate : function()
32486     {
32487         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32488         
32489         cfg = {
32490             tag : 'div',
32491             cls : 'roo-navigation-bar-group',
32492             cn : [
32493                 {
32494                     tag : 'div',
32495                     cls : 'roo-navigation-top-bar'
32496                 },
32497                 {
32498                     tag : 'div',
32499                     cls : 'roo-navigation-bullets-bar',
32500                     cn : [
32501                         {
32502                             tag : 'ul',
32503                             cls : 'roo-navigation-bar'
32504                         }
32505                     ]
32506                 },
32507                 
32508                 {
32509                     tag : 'div',
32510                     cls : 'roo-navigation-bottom-bar'
32511                 }
32512             ]
32513             
32514         };
32515         
32516         return cfg;
32517         
32518     },
32519     
32520     initEvents: function() 
32521     {
32522         
32523     },
32524     
32525     onRender : function(ct, position) 
32526     {
32527         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32528         
32529         if(this.bullets.length){
32530             Roo.each(this.bullets, function(b){
32531                this.addItem(b);
32532             }, this);
32533         }
32534         
32535         this.format();
32536         
32537     },
32538     
32539     addItem : function(cfg)
32540     {
32541         var item = new Roo.bootstrap.NavProgressItem(cfg);
32542         
32543         item.parentId = this.id;
32544         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32545         
32546         if(cfg.html){
32547             var top = new Roo.bootstrap.Element({
32548                 tag : 'div',
32549                 cls : 'roo-navigation-bar-text'
32550             });
32551             
32552             var bottom = new Roo.bootstrap.Element({
32553                 tag : 'div',
32554                 cls : 'roo-navigation-bar-text'
32555             });
32556             
32557             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32558             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32559             
32560             var topText = new Roo.bootstrap.Element({
32561                 tag : 'span',
32562                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32563             });
32564             
32565             var bottomText = new Roo.bootstrap.Element({
32566                 tag : 'span',
32567                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32568             });
32569             
32570             topText.onRender(top.el, null);
32571             bottomText.onRender(bottom.el, null);
32572             
32573             item.topEl = top;
32574             item.bottomEl = bottom;
32575         }
32576         
32577         this.barItems.push(item);
32578         
32579         return item;
32580     },
32581     
32582     getActive : function()
32583     {
32584         var active = false;
32585         
32586         Roo.each(this.barItems, function(v){
32587             
32588             if (!v.isActive()) {
32589                 return;
32590             }
32591             
32592             active = v;
32593             return false;
32594             
32595         });
32596         
32597         return active;
32598     },
32599     
32600     setActiveItem : function(item)
32601     {
32602         var prev = false;
32603         
32604         Roo.each(this.barItems, function(v){
32605             if (v.rid == item.rid) {
32606                 return ;
32607             }
32608             
32609             if (v.isActive()) {
32610                 v.setActive(false);
32611                 prev = v;
32612             }
32613         });
32614
32615         item.setActive(true);
32616         
32617         this.fireEvent('changed', this, item, prev);
32618     },
32619     
32620     getBarItem: function(rid)
32621     {
32622         var ret = false;
32623         
32624         Roo.each(this.barItems, function(e) {
32625             if (e.rid != rid) {
32626                 return;
32627             }
32628             
32629             ret =  e;
32630             return false;
32631         });
32632         
32633         return ret;
32634     },
32635     
32636     indexOfItem : function(item)
32637     {
32638         var index = false;
32639         
32640         Roo.each(this.barItems, function(v, i){
32641             
32642             if (v.rid != item.rid) {
32643                 return;
32644             }
32645             
32646             index = i;
32647             return false
32648         });
32649         
32650         return index;
32651     },
32652     
32653     setActiveNext : function()
32654     {
32655         var i = this.indexOfItem(this.getActive());
32656         
32657         if (i > this.barItems.length) {
32658             return;
32659         }
32660         
32661         this.setActiveItem(this.barItems[i+1]);
32662     },
32663     
32664     setActivePrev : function()
32665     {
32666         var i = this.indexOfItem(this.getActive());
32667         
32668         if (i  < 1) {
32669             return;
32670         }
32671         
32672         this.setActiveItem(this.barItems[i-1]);
32673     },
32674     
32675     format : function()
32676     {
32677         if(!this.barItems.length){
32678             return;
32679         }
32680      
32681         var width = 100 / this.barItems.length;
32682         
32683         Roo.each(this.barItems, function(i){
32684             i.el.setStyle('width', width + '%');
32685             i.topEl.el.setStyle('width', width + '%');
32686             i.bottomEl.el.setStyle('width', width + '%');
32687         }, this);
32688         
32689     }
32690     
32691 });
32692 /*
32693  * - LGPL
32694  *
32695  * Nav Progress Item
32696  * 
32697  */
32698
32699 /**
32700  * @class Roo.bootstrap.NavProgressItem
32701  * @extends Roo.bootstrap.Component
32702  * Bootstrap NavProgressItem class
32703  * @cfg {String} rid the reference id
32704  * @cfg {Boolean} active (true|false) Is item active default false
32705  * @cfg {Boolean} disabled (true|false) Is item active default false
32706  * @cfg {String} html
32707  * @cfg {String} position (top|bottom) text position default bottom
32708  * @cfg {String} icon show icon instead of number
32709  * 
32710  * @constructor
32711  * Create a new NavProgressItem
32712  * @param {Object} config The config object
32713  */
32714 Roo.bootstrap.NavProgressItem = function(config){
32715     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32716     this.addEvents({
32717         // raw events
32718         /**
32719          * @event click
32720          * The raw click event for the entire grid.
32721          * @param {Roo.bootstrap.NavProgressItem} this
32722          * @param {Roo.EventObject} e
32723          */
32724         "click" : true
32725     });
32726    
32727 };
32728
32729 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32730     
32731     rid : '',
32732     active : false,
32733     disabled : false,
32734     html : '',
32735     position : 'bottom',
32736     icon : false,
32737     
32738     getAutoCreate : function()
32739     {
32740         var iconCls = 'roo-navigation-bar-item-icon';
32741         
32742         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32743         
32744         var cfg = {
32745             tag: 'li',
32746             cls: 'roo-navigation-bar-item',
32747             cn : [
32748                 {
32749                     tag : 'i',
32750                     cls : iconCls
32751                 }
32752             ]
32753         };
32754         
32755         if(this.active){
32756             cfg.cls += ' active';
32757         }
32758         if(this.disabled){
32759             cfg.cls += ' disabled';
32760         }
32761         
32762         return cfg;
32763     },
32764     
32765     disable : function()
32766     {
32767         this.setDisabled(true);
32768     },
32769     
32770     enable : function()
32771     {
32772         this.setDisabled(false);
32773     },
32774     
32775     initEvents: function() 
32776     {
32777         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32778         
32779         this.iconEl.on('click', this.onClick, this);
32780     },
32781     
32782     onClick : function(e)
32783     {
32784         e.preventDefault();
32785         
32786         if(this.disabled){
32787             return;
32788         }
32789         
32790         if(this.fireEvent('click', this, e) === false){
32791             return;
32792         };
32793         
32794         this.parent().setActiveItem(this);
32795     },
32796     
32797     isActive: function () 
32798     {
32799         return this.active;
32800     },
32801     
32802     setActive : function(state)
32803     {
32804         if(this.active == state){
32805             return;
32806         }
32807         
32808         this.active = state;
32809         
32810         if (state) {
32811             this.el.addClass('active');
32812             return;
32813         }
32814         
32815         this.el.removeClass('active');
32816         
32817         return;
32818     },
32819     
32820     setDisabled : function(state)
32821     {
32822         if(this.disabled == state){
32823             return;
32824         }
32825         
32826         this.disabled = state;
32827         
32828         if (state) {
32829             this.el.addClass('disabled');
32830             return;
32831         }
32832         
32833         this.el.removeClass('disabled');
32834     },
32835     
32836     tooltipEl : function()
32837     {
32838         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32839     }
32840 });
32841  
32842
32843  /*
32844  * - LGPL
32845  *
32846  * FieldLabel
32847  * 
32848  */
32849
32850 /**
32851  * @class Roo.bootstrap.FieldLabel
32852  * @extends Roo.bootstrap.Component
32853  * Bootstrap FieldLabel class
32854  * @cfg {String} html contents of the element
32855  * @cfg {String} tag tag of the element default label
32856  * @cfg {String} cls class of the element
32857  * @cfg {String} target label target 
32858  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32859  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32860  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32861  * @cfg {String} iconTooltip default "This field is required"
32862  * @cfg {String} indicatorpos (left|right) default left
32863  * 
32864  * @constructor
32865  * Create a new FieldLabel
32866  * @param {Object} config The config object
32867  */
32868
32869 Roo.bootstrap.FieldLabel = function(config){
32870     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32871     
32872     this.addEvents({
32873             /**
32874              * @event invalid
32875              * Fires after the field has been marked as invalid.
32876              * @param {Roo.form.FieldLabel} this
32877              * @param {String} msg The validation message
32878              */
32879             invalid : true,
32880             /**
32881              * @event valid
32882              * Fires after the field has been validated with no errors.
32883              * @param {Roo.form.FieldLabel} this
32884              */
32885             valid : true
32886         });
32887 };
32888
32889 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32890     
32891     tag: 'label',
32892     cls: '',
32893     html: '',
32894     target: '',
32895     allowBlank : true,
32896     invalidClass : 'has-warning',
32897     validClass : 'has-success',
32898     iconTooltip : 'This field is required',
32899     indicatorpos : 'left',
32900     
32901     getAutoCreate : function(){
32902         
32903         var cls = "";
32904         if (!this.allowBlank) {
32905             cls  = "visible";
32906         }
32907         
32908         var cfg = {
32909             tag : this.tag,
32910             cls : 'roo-bootstrap-field-label ' + this.cls,
32911             for : this.target,
32912             cn : [
32913                 {
32914                     tag : 'i',
32915                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32916                     tooltip : this.iconTooltip
32917                 },
32918                 {
32919                     tag : 'span',
32920                     html : this.html
32921                 }
32922             ] 
32923         };
32924         
32925         if(this.indicatorpos == 'right'){
32926             var cfg = {
32927                 tag : this.tag,
32928                 cls : 'roo-bootstrap-field-label ' + this.cls,
32929                 for : this.target,
32930                 cn : [
32931                     {
32932                         tag : 'span',
32933                         html : this.html
32934                     },
32935                     {
32936                         tag : 'i',
32937                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32938                         tooltip : this.iconTooltip
32939                     }
32940                 ] 
32941             };
32942         }
32943         
32944         return cfg;
32945     },
32946     
32947     initEvents: function() 
32948     {
32949         Roo.bootstrap.Element.superclass.initEvents.call(this);
32950         
32951         this.indicator = this.indicatorEl();
32952         
32953         if(this.indicator){
32954             this.indicator.removeClass('visible');
32955             this.indicator.addClass('invisible');
32956         }
32957         
32958         Roo.bootstrap.FieldLabel.register(this);
32959     },
32960     
32961     indicatorEl : function()
32962     {
32963         var indicator = this.el.select('i.roo-required-indicator',true).first();
32964         
32965         if(!indicator){
32966             return false;
32967         }
32968         
32969         return indicator;
32970         
32971     },
32972     
32973     /**
32974      * Mark this field as valid
32975      */
32976     markValid : function()
32977     {
32978         if(this.indicator){
32979             this.indicator.removeClass('visible');
32980             this.indicator.addClass('invisible');
32981         }
32982         if (Roo.bootstrap.version == 3) {
32983             this.el.removeClass(this.invalidClass);
32984             this.el.addClass(this.validClass);
32985         } else {
32986             this.el.removeClass('is-invalid');
32987             this.el.addClass('is-valid');
32988         }
32989         
32990         
32991         this.fireEvent('valid', this);
32992     },
32993     
32994     /**
32995      * Mark this field as invalid
32996      * @param {String} msg The validation message
32997      */
32998     markInvalid : function(msg)
32999     {
33000         if(this.indicator){
33001             this.indicator.removeClass('invisible');
33002             this.indicator.addClass('visible');
33003         }
33004           if (Roo.bootstrap.version == 3) {
33005             this.el.removeClass(this.validClass);
33006             this.el.addClass(this.invalidClass);
33007         } else {
33008             this.el.removeClass('is-valid');
33009             this.el.addClass('is-invalid');
33010         }
33011         
33012         
33013         this.fireEvent('invalid', this, msg);
33014     }
33015     
33016    
33017 });
33018
33019 Roo.apply(Roo.bootstrap.FieldLabel, {
33020     
33021     groups: {},
33022     
33023      /**
33024     * register a FieldLabel Group
33025     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33026     */
33027     register : function(label)
33028     {
33029         if(this.groups.hasOwnProperty(label.target)){
33030             return;
33031         }
33032      
33033         this.groups[label.target] = label;
33034         
33035     },
33036     /**
33037     * fetch a FieldLabel Group based on the target
33038     * @param {string} target
33039     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33040     */
33041     get: function(target) {
33042         if (typeof(this.groups[target]) == 'undefined') {
33043             return false;
33044         }
33045         
33046         return this.groups[target] ;
33047     }
33048 });
33049
33050  
33051
33052  /*
33053  * - LGPL
33054  *
33055  * page DateSplitField.
33056  * 
33057  */
33058
33059
33060 /**
33061  * @class Roo.bootstrap.DateSplitField
33062  * @extends Roo.bootstrap.Component
33063  * Bootstrap DateSplitField class
33064  * @cfg {string} fieldLabel - the label associated
33065  * @cfg {Number} labelWidth set the width of label (0-12)
33066  * @cfg {String} labelAlign (top|left)
33067  * @cfg {Boolean} dayAllowBlank (true|false) default false
33068  * @cfg {Boolean} monthAllowBlank (true|false) default false
33069  * @cfg {Boolean} yearAllowBlank (true|false) default false
33070  * @cfg {string} dayPlaceholder 
33071  * @cfg {string} monthPlaceholder
33072  * @cfg {string} yearPlaceholder
33073  * @cfg {string} dayFormat default 'd'
33074  * @cfg {string} monthFormat default 'm'
33075  * @cfg {string} yearFormat default 'Y'
33076  * @cfg {Number} labellg set the width of label (1-12)
33077  * @cfg {Number} labelmd set the width of label (1-12)
33078  * @cfg {Number} labelsm set the width of label (1-12)
33079  * @cfg {Number} labelxs set the width of label (1-12)
33080
33081  *     
33082  * @constructor
33083  * Create a new DateSplitField
33084  * @param {Object} config The config object
33085  */
33086
33087 Roo.bootstrap.DateSplitField = function(config){
33088     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33089     
33090     this.addEvents({
33091         // raw events
33092          /**
33093          * @event years
33094          * getting the data of years
33095          * @param {Roo.bootstrap.DateSplitField} this
33096          * @param {Object} years
33097          */
33098         "years" : true,
33099         /**
33100          * @event days
33101          * getting the data of days
33102          * @param {Roo.bootstrap.DateSplitField} this
33103          * @param {Object} days
33104          */
33105         "days" : true,
33106         /**
33107          * @event invalid
33108          * Fires after the field has been marked as invalid.
33109          * @param {Roo.form.Field} this
33110          * @param {String} msg The validation message
33111          */
33112         invalid : true,
33113        /**
33114          * @event valid
33115          * Fires after the field has been validated with no errors.
33116          * @param {Roo.form.Field} this
33117          */
33118         valid : true
33119     });
33120 };
33121
33122 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33123     
33124     fieldLabel : '',
33125     labelAlign : 'top',
33126     labelWidth : 3,
33127     dayAllowBlank : false,
33128     monthAllowBlank : false,
33129     yearAllowBlank : false,
33130     dayPlaceholder : '',
33131     monthPlaceholder : '',
33132     yearPlaceholder : '',
33133     dayFormat : 'd',
33134     monthFormat : 'm',
33135     yearFormat : 'Y',
33136     isFormField : true,
33137     labellg : 0,
33138     labelmd : 0,
33139     labelsm : 0,
33140     labelxs : 0,
33141     
33142     getAutoCreate : function()
33143     {
33144         var cfg = {
33145             tag : 'div',
33146             cls : 'row roo-date-split-field-group',
33147             cn : [
33148                 {
33149                     tag : 'input',
33150                     type : 'hidden',
33151                     cls : 'form-hidden-field roo-date-split-field-group-value',
33152                     name : this.name
33153                 }
33154             ]
33155         };
33156         
33157         var labelCls = 'col-md-12';
33158         var contentCls = 'col-md-4';
33159         
33160         if(this.fieldLabel){
33161             
33162             var label = {
33163                 tag : 'div',
33164                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33165                 cn : [
33166                     {
33167                         tag : 'label',
33168                         html : this.fieldLabel
33169                     }
33170                 ]
33171             };
33172             
33173             if(this.labelAlign == 'left'){
33174             
33175                 if(this.labelWidth > 12){
33176                     label.style = "width: " + this.labelWidth + 'px';
33177                 }
33178
33179                 if(this.labelWidth < 13 && this.labelmd == 0){
33180                     this.labelmd = this.labelWidth;
33181                 }
33182
33183                 if(this.labellg > 0){
33184                     labelCls = ' col-lg-' + this.labellg;
33185                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33186                 }
33187
33188                 if(this.labelmd > 0){
33189                     labelCls = ' col-md-' + this.labelmd;
33190                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33191                 }
33192
33193                 if(this.labelsm > 0){
33194                     labelCls = ' col-sm-' + this.labelsm;
33195                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33196                 }
33197
33198                 if(this.labelxs > 0){
33199                     labelCls = ' col-xs-' + this.labelxs;
33200                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33201                 }
33202             }
33203             
33204             label.cls += ' ' + labelCls;
33205             
33206             cfg.cn.push(label);
33207         }
33208         
33209         Roo.each(['day', 'month', 'year'], function(t){
33210             cfg.cn.push({
33211                 tag : 'div',
33212                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33213             });
33214         }, this);
33215         
33216         return cfg;
33217     },
33218     
33219     inputEl: function ()
33220     {
33221         return this.el.select('.roo-date-split-field-group-value', true).first();
33222     },
33223     
33224     onRender : function(ct, position) 
33225     {
33226         var _this = this;
33227         
33228         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33229         
33230         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33231         
33232         this.dayField = new Roo.bootstrap.ComboBox({
33233             allowBlank : this.dayAllowBlank,
33234             alwaysQuery : true,
33235             displayField : 'value',
33236             editable : false,
33237             fieldLabel : '',
33238             forceSelection : true,
33239             mode : 'local',
33240             placeholder : this.dayPlaceholder,
33241             selectOnFocus : true,
33242             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33243             triggerAction : 'all',
33244             typeAhead : true,
33245             valueField : 'value',
33246             store : new Roo.data.SimpleStore({
33247                 data : (function() {    
33248                     var days = [];
33249                     _this.fireEvent('days', _this, days);
33250                     return days;
33251                 })(),
33252                 fields : [ 'value' ]
33253             }),
33254             listeners : {
33255                 select : function (_self, record, index)
33256                 {
33257                     _this.setValue(_this.getValue());
33258                 }
33259             }
33260         });
33261
33262         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33263         
33264         this.monthField = new Roo.bootstrap.MonthField({
33265             after : '<i class=\"fa fa-calendar\"></i>',
33266             allowBlank : this.monthAllowBlank,
33267             placeholder : this.monthPlaceholder,
33268             readOnly : true,
33269             listeners : {
33270                 render : function (_self)
33271                 {
33272                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33273                         e.preventDefault();
33274                         _self.focus();
33275                     });
33276                 },
33277                 select : function (_self, oldvalue, newvalue)
33278                 {
33279                     _this.setValue(_this.getValue());
33280                 }
33281             }
33282         });
33283         
33284         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33285         
33286         this.yearField = new Roo.bootstrap.ComboBox({
33287             allowBlank : this.yearAllowBlank,
33288             alwaysQuery : true,
33289             displayField : 'value',
33290             editable : false,
33291             fieldLabel : '',
33292             forceSelection : true,
33293             mode : 'local',
33294             placeholder : this.yearPlaceholder,
33295             selectOnFocus : true,
33296             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33297             triggerAction : 'all',
33298             typeAhead : true,
33299             valueField : 'value',
33300             store : new Roo.data.SimpleStore({
33301                 data : (function() {
33302                     var years = [];
33303                     _this.fireEvent('years', _this, years);
33304                     return years;
33305                 })(),
33306                 fields : [ 'value' ]
33307             }),
33308             listeners : {
33309                 select : function (_self, record, index)
33310                 {
33311                     _this.setValue(_this.getValue());
33312                 }
33313             }
33314         });
33315
33316         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33317     },
33318     
33319     setValue : function(v, format)
33320     {
33321         this.inputEl.dom.value = v;
33322         
33323         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33324         
33325         var d = Date.parseDate(v, f);
33326         
33327         if(!d){
33328             this.validate();
33329             return;
33330         }
33331         
33332         this.setDay(d.format(this.dayFormat));
33333         this.setMonth(d.format(this.monthFormat));
33334         this.setYear(d.format(this.yearFormat));
33335         
33336         this.validate();
33337         
33338         return;
33339     },
33340     
33341     setDay : function(v)
33342     {
33343         this.dayField.setValue(v);
33344         this.inputEl.dom.value = this.getValue();
33345         this.validate();
33346         return;
33347     },
33348     
33349     setMonth : function(v)
33350     {
33351         this.monthField.setValue(v, true);
33352         this.inputEl.dom.value = this.getValue();
33353         this.validate();
33354         return;
33355     },
33356     
33357     setYear : function(v)
33358     {
33359         this.yearField.setValue(v);
33360         this.inputEl.dom.value = this.getValue();
33361         this.validate();
33362         return;
33363     },
33364     
33365     getDay : function()
33366     {
33367         return this.dayField.getValue();
33368     },
33369     
33370     getMonth : function()
33371     {
33372         return this.monthField.getValue();
33373     },
33374     
33375     getYear : function()
33376     {
33377         return this.yearField.getValue();
33378     },
33379     
33380     getValue : function()
33381     {
33382         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33383         
33384         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33385         
33386         return date;
33387     },
33388     
33389     reset : function()
33390     {
33391         this.setDay('');
33392         this.setMonth('');
33393         this.setYear('');
33394         this.inputEl.dom.value = '';
33395         this.validate();
33396         return;
33397     },
33398     
33399     validate : function()
33400     {
33401         var d = this.dayField.validate();
33402         var m = this.monthField.validate();
33403         var y = this.yearField.validate();
33404         
33405         var valid = true;
33406         
33407         if(
33408                 (!this.dayAllowBlank && !d) ||
33409                 (!this.monthAllowBlank && !m) ||
33410                 (!this.yearAllowBlank && !y)
33411         ){
33412             valid = false;
33413         }
33414         
33415         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33416             return valid;
33417         }
33418         
33419         if(valid){
33420             this.markValid();
33421             return valid;
33422         }
33423         
33424         this.markInvalid();
33425         
33426         return valid;
33427     },
33428     
33429     markValid : function()
33430     {
33431         
33432         var label = this.el.select('label', true).first();
33433         var icon = this.el.select('i.fa-star', true).first();
33434
33435         if(label && icon){
33436             icon.remove();
33437         }
33438         
33439         this.fireEvent('valid', this);
33440     },
33441     
33442      /**
33443      * Mark this field as invalid
33444      * @param {String} msg The validation message
33445      */
33446     markInvalid : function(msg)
33447     {
33448         
33449         var label = this.el.select('label', true).first();
33450         var icon = this.el.select('i.fa-star', true).first();
33451
33452         if(label && !icon){
33453             this.el.select('.roo-date-split-field-label', true).createChild({
33454                 tag : 'i',
33455                 cls : 'text-danger fa fa-lg fa-star',
33456                 tooltip : 'This field is required',
33457                 style : 'margin-right:5px;'
33458             }, label, true);
33459         }
33460         
33461         this.fireEvent('invalid', this, msg);
33462     },
33463     
33464     clearInvalid : function()
33465     {
33466         var label = this.el.select('label', true).first();
33467         var icon = this.el.select('i.fa-star', true).first();
33468
33469         if(label && icon){
33470             icon.remove();
33471         }
33472         
33473         this.fireEvent('valid', this);
33474     },
33475     
33476     getName: function()
33477     {
33478         return this.name;
33479     }
33480     
33481 });
33482
33483  /**
33484  *
33485  * This is based on 
33486  * http://masonry.desandro.com
33487  *
33488  * The idea is to render all the bricks based on vertical width...
33489  *
33490  * The original code extends 'outlayer' - we might need to use that....
33491  * 
33492  */
33493
33494
33495 /**
33496  * @class Roo.bootstrap.LayoutMasonry
33497  * @extends Roo.bootstrap.Component
33498  * Bootstrap Layout Masonry class
33499  * 
33500  * @constructor
33501  * Create a new Element
33502  * @param {Object} config The config object
33503  */
33504
33505 Roo.bootstrap.LayoutMasonry = function(config){
33506     
33507     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33508     
33509     this.bricks = [];
33510     
33511     Roo.bootstrap.LayoutMasonry.register(this);
33512     
33513     this.addEvents({
33514         // raw events
33515         /**
33516          * @event layout
33517          * Fire after layout the items
33518          * @param {Roo.bootstrap.LayoutMasonry} this
33519          * @param {Roo.EventObject} e
33520          */
33521         "layout" : true
33522     });
33523     
33524 };
33525
33526 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33527     
33528     /**
33529      * @cfg {Boolean} isLayoutInstant = no animation?
33530      */   
33531     isLayoutInstant : false, // needed?
33532    
33533     /**
33534      * @cfg {Number} boxWidth  width of the columns
33535      */   
33536     boxWidth : 450,
33537     
33538       /**
33539      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33540      */   
33541     boxHeight : 0,
33542     
33543     /**
33544      * @cfg {Number} padWidth padding below box..
33545      */   
33546     padWidth : 10, 
33547     
33548     /**
33549      * @cfg {Number} gutter gutter width..
33550      */   
33551     gutter : 10,
33552     
33553      /**
33554      * @cfg {Number} maxCols maximum number of columns
33555      */   
33556     
33557     maxCols: 0,
33558     
33559     /**
33560      * @cfg {Boolean} isAutoInitial defalut true
33561      */   
33562     isAutoInitial : true, 
33563     
33564     containerWidth: 0,
33565     
33566     /**
33567      * @cfg {Boolean} isHorizontal defalut false
33568      */   
33569     isHorizontal : false, 
33570
33571     currentSize : null,
33572     
33573     tag: 'div',
33574     
33575     cls: '',
33576     
33577     bricks: null, //CompositeElement
33578     
33579     cols : 1,
33580     
33581     _isLayoutInited : false,
33582     
33583 //    isAlternative : false, // only use for vertical layout...
33584     
33585     /**
33586      * @cfg {Number} alternativePadWidth padding below box..
33587      */   
33588     alternativePadWidth : 50,
33589     
33590     selectedBrick : [],
33591     
33592     getAutoCreate : function(){
33593         
33594         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33595         
33596         var cfg = {
33597             tag: this.tag,
33598             cls: 'blog-masonary-wrapper ' + this.cls,
33599             cn : {
33600                 cls : 'mas-boxes masonary'
33601             }
33602         };
33603         
33604         return cfg;
33605     },
33606     
33607     getChildContainer: function( )
33608     {
33609         if (this.boxesEl) {
33610             return this.boxesEl;
33611         }
33612         
33613         this.boxesEl = this.el.select('.mas-boxes').first();
33614         
33615         return this.boxesEl;
33616     },
33617     
33618     
33619     initEvents : function()
33620     {
33621         var _this = this;
33622         
33623         if(this.isAutoInitial){
33624             Roo.log('hook children rendered');
33625             this.on('childrenrendered', function() {
33626                 Roo.log('children rendered');
33627                 _this.initial();
33628             } ,this);
33629         }
33630     },
33631     
33632     initial : function()
33633     {
33634         this.selectedBrick = [];
33635         
33636         this.currentSize = this.el.getBox(true);
33637         
33638         Roo.EventManager.onWindowResize(this.resize, this); 
33639
33640         if(!this.isAutoInitial){
33641             this.layout();
33642             return;
33643         }
33644         
33645         this.layout();
33646         
33647         return;
33648         //this.layout.defer(500,this);
33649         
33650     },
33651     
33652     resize : function()
33653     {
33654         var cs = this.el.getBox(true);
33655         
33656         if (
33657                 this.currentSize.width == cs.width && 
33658                 this.currentSize.x == cs.x && 
33659                 this.currentSize.height == cs.height && 
33660                 this.currentSize.y == cs.y 
33661         ) {
33662             Roo.log("no change in with or X or Y");
33663             return;
33664         }
33665         
33666         this.currentSize = cs;
33667         
33668         this.layout();
33669         
33670     },
33671     
33672     layout : function()
33673     {   
33674         this._resetLayout();
33675         
33676         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33677         
33678         this.layoutItems( isInstant );
33679       
33680         this._isLayoutInited = true;
33681         
33682         this.fireEvent('layout', this);
33683         
33684     },
33685     
33686     _resetLayout : function()
33687     {
33688         if(this.isHorizontal){
33689             this.horizontalMeasureColumns();
33690             return;
33691         }
33692         
33693         this.verticalMeasureColumns();
33694         
33695     },
33696     
33697     verticalMeasureColumns : function()
33698     {
33699         this.getContainerWidth();
33700         
33701 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33702 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33703 //            return;
33704 //        }
33705         
33706         var boxWidth = this.boxWidth + this.padWidth;
33707         
33708         if(this.containerWidth < this.boxWidth){
33709             boxWidth = this.containerWidth
33710         }
33711         
33712         var containerWidth = this.containerWidth;
33713         
33714         var cols = Math.floor(containerWidth / boxWidth);
33715         
33716         this.cols = Math.max( cols, 1 );
33717         
33718         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33719         
33720         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33721         
33722         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33723         
33724         this.colWidth = boxWidth + avail - this.padWidth;
33725         
33726         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33727         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33728     },
33729     
33730     horizontalMeasureColumns : function()
33731     {
33732         this.getContainerWidth();
33733         
33734         var boxWidth = this.boxWidth;
33735         
33736         if(this.containerWidth < boxWidth){
33737             boxWidth = this.containerWidth;
33738         }
33739         
33740         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33741         
33742         this.el.setHeight(boxWidth);
33743         
33744     },
33745     
33746     getContainerWidth : function()
33747     {
33748         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33749     },
33750     
33751     layoutItems : function( isInstant )
33752     {
33753         Roo.log(this.bricks);
33754         
33755         var items = Roo.apply([], this.bricks);
33756         
33757         if(this.isHorizontal){
33758             this._horizontalLayoutItems( items , isInstant );
33759             return;
33760         }
33761         
33762 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33763 //            this._verticalAlternativeLayoutItems( items , isInstant );
33764 //            return;
33765 //        }
33766         
33767         this._verticalLayoutItems( items , isInstant );
33768         
33769     },
33770     
33771     _verticalLayoutItems : function ( items , isInstant)
33772     {
33773         if ( !items || !items.length ) {
33774             return;
33775         }
33776         
33777         var standard = [
33778             ['xs', 'xs', 'xs', 'tall'],
33779             ['xs', 'xs', 'tall'],
33780             ['xs', 'xs', 'sm'],
33781             ['xs', 'xs', 'xs'],
33782             ['xs', 'tall'],
33783             ['xs', 'sm'],
33784             ['xs', 'xs'],
33785             ['xs'],
33786             
33787             ['sm', 'xs', 'xs'],
33788             ['sm', 'xs'],
33789             ['sm'],
33790             
33791             ['tall', 'xs', 'xs', 'xs'],
33792             ['tall', 'xs', 'xs'],
33793             ['tall', 'xs'],
33794             ['tall']
33795             
33796         ];
33797         
33798         var queue = [];
33799         
33800         var boxes = [];
33801         
33802         var box = [];
33803         
33804         Roo.each(items, function(item, k){
33805             
33806             switch (item.size) {
33807                 // these layouts take up a full box,
33808                 case 'md' :
33809                 case 'md-left' :
33810                 case 'md-right' :
33811                 case 'wide' :
33812                     
33813                     if(box.length){
33814                         boxes.push(box);
33815                         box = [];
33816                     }
33817                     
33818                     boxes.push([item]);
33819                     
33820                     break;
33821                     
33822                 case 'xs' :
33823                 case 'sm' :
33824                 case 'tall' :
33825                     
33826                     box.push(item);
33827                     
33828                     break;
33829                 default :
33830                     break;
33831                     
33832             }
33833             
33834         }, this);
33835         
33836         if(box.length){
33837             boxes.push(box);
33838             box = [];
33839         }
33840         
33841         var filterPattern = function(box, length)
33842         {
33843             if(!box.length){
33844                 return;
33845             }
33846             
33847             var match = false;
33848             
33849             var pattern = box.slice(0, length);
33850             
33851             var format = [];
33852             
33853             Roo.each(pattern, function(i){
33854                 format.push(i.size);
33855             }, this);
33856             
33857             Roo.each(standard, function(s){
33858                 
33859                 if(String(s) != String(format)){
33860                     return;
33861                 }
33862                 
33863                 match = true;
33864                 return false;
33865                 
33866             }, this);
33867             
33868             if(!match && length == 1){
33869                 return;
33870             }
33871             
33872             if(!match){
33873                 filterPattern(box, length - 1);
33874                 return;
33875             }
33876                 
33877             queue.push(pattern);
33878
33879             box = box.slice(length, box.length);
33880
33881             filterPattern(box, 4);
33882
33883             return;
33884             
33885         }
33886         
33887         Roo.each(boxes, function(box, k){
33888             
33889             if(!box.length){
33890                 return;
33891             }
33892             
33893             if(box.length == 1){
33894                 queue.push(box);
33895                 return;
33896             }
33897             
33898             filterPattern(box, 4);
33899             
33900         }, this);
33901         
33902         this._processVerticalLayoutQueue( queue, isInstant );
33903         
33904     },
33905     
33906 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33907 //    {
33908 //        if ( !items || !items.length ) {
33909 //            return;
33910 //        }
33911 //
33912 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33913 //        
33914 //    },
33915     
33916     _horizontalLayoutItems : function ( items , isInstant)
33917     {
33918         if ( !items || !items.length || items.length < 3) {
33919             return;
33920         }
33921         
33922         items.reverse();
33923         
33924         var eItems = items.slice(0, 3);
33925         
33926         items = items.slice(3, items.length);
33927         
33928         var standard = [
33929             ['xs', 'xs', 'xs', 'wide'],
33930             ['xs', 'xs', 'wide'],
33931             ['xs', 'xs', 'sm'],
33932             ['xs', 'xs', 'xs'],
33933             ['xs', 'wide'],
33934             ['xs', 'sm'],
33935             ['xs', 'xs'],
33936             ['xs'],
33937             
33938             ['sm', 'xs', 'xs'],
33939             ['sm', 'xs'],
33940             ['sm'],
33941             
33942             ['wide', 'xs', 'xs', 'xs'],
33943             ['wide', 'xs', 'xs'],
33944             ['wide', 'xs'],
33945             ['wide'],
33946             
33947             ['wide-thin']
33948         ];
33949         
33950         var queue = [];
33951         
33952         var boxes = [];
33953         
33954         var box = [];
33955         
33956         Roo.each(items, function(item, k){
33957             
33958             switch (item.size) {
33959                 case 'md' :
33960                 case 'md-left' :
33961                 case 'md-right' :
33962                 case 'tall' :
33963                     
33964                     if(box.length){
33965                         boxes.push(box);
33966                         box = [];
33967                     }
33968                     
33969                     boxes.push([item]);
33970                     
33971                     break;
33972                     
33973                 case 'xs' :
33974                 case 'sm' :
33975                 case 'wide' :
33976                 case 'wide-thin' :
33977                     
33978                     box.push(item);
33979                     
33980                     break;
33981                 default :
33982                     break;
33983                     
33984             }
33985             
33986         }, this);
33987         
33988         if(box.length){
33989             boxes.push(box);
33990             box = [];
33991         }
33992         
33993         var filterPattern = function(box, length)
33994         {
33995             if(!box.length){
33996                 return;
33997             }
33998             
33999             var match = false;
34000             
34001             var pattern = box.slice(0, length);
34002             
34003             var format = [];
34004             
34005             Roo.each(pattern, function(i){
34006                 format.push(i.size);
34007             }, this);
34008             
34009             Roo.each(standard, function(s){
34010                 
34011                 if(String(s) != String(format)){
34012                     return;
34013                 }
34014                 
34015                 match = true;
34016                 return false;
34017                 
34018             }, this);
34019             
34020             if(!match && length == 1){
34021                 return;
34022             }
34023             
34024             if(!match){
34025                 filterPattern(box, length - 1);
34026                 return;
34027             }
34028                 
34029             queue.push(pattern);
34030
34031             box = box.slice(length, box.length);
34032
34033             filterPattern(box, 4);
34034
34035             return;
34036             
34037         }
34038         
34039         Roo.each(boxes, function(box, k){
34040             
34041             if(!box.length){
34042                 return;
34043             }
34044             
34045             if(box.length == 1){
34046                 queue.push(box);
34047                 return;
34048             }
34049             
34050             filterPattern(box, 4);
34051             
34052         }, this);
34053         
34054         
34055         var prune = [];
34056         
34057         var pos = this.el.getBox(true);
34058         
34059         var minX = pos.x;
34060         
34061         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34062         
34063         var hit_end = false;
34064         
34065         Roo.each(queue, function(box){
34066             
34067             if(hit_end){
34068                 
34069                 Roo.each(box, function(b){
34070                 
34071                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34072                     b.el.hide();
34073
34074                 }, this);
34075
34076                 return;
34077             }
34078             
34079             var mx = 0;
34080             
34081             Roo.each(box, function(b){
34082                 
34083                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34084                 b.el.show();
34085
34086                 mx = Math.max(mx, b.x);
34087                 
34088             }, this);
34089             
34090             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34091             
34092             if(maxX < minX){
34093                 
34094                 Roo.each(box, function(b){
34095                 
34096                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34097                     b.el.hide();
34098                     
34099                 }, this);
34100                 
34101                 hit_end = true;
34102                 
34103                 return;
34104             }
34105             
34106             prune.push(box);
34107             
34108         }, this);
34109         
34110         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34111     },
34112     
34113     /** Sets position of item in DOM
34114     * @param {Element} item
34115     * @param {Number} x - horizontal position
34116     * @param {Number} y - vertical position
34117     * @param {Boolean} isInstant - disables transitions
34118     */
34119     _processVerticalLayoutQueue : function( queue, isInstant )
34120     {
34121         var pos = this.el.getBox(true);
34122         var x = pos.x;
34123         var y = pos.y;
34124         var maxY = [];
34125         
34126         for (var i = 0; i < this.cols; i++){
34127             maxY[i] = pos.y;
34128         }
34129         
34130         Roo.each(queue, function(box, k){
34131             
34132             var col = k % this.cols;
34133             
34134             Roo.each(box, function(b,kk){
34135                 
34136                 b.el.position('absolute');
34137                 
34138                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34139                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34140                 
34141                 if(b.size == 'md-left' || b.size == 'md-right'){
34142                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34143                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34144                 }
34145                 
34146                 b.el.setWidth(width);
34147                 b.el.setHeight(height);
34148                 // iframe?
34149                 b.el.select('iframe',true).setSize(width,height);
34150                 
34151             }, this);
34152             
34153             for (var i = 0; i < this.cols; i++){
34154                 
34155                 if(maxY[i] < maxY[col]){
34156                     col = i;
34157                     continue;
34158                 }
34159                 
34160                 col = Math.min(col, i);
34161                 
34162             }
34163             
34164             x = pos.x + col * (this.colWidth + this.padWidth);
34165             
34166             y = maxY[col];
34167             
34168             var positions = [];
34169             
34170             switch (box.length){
34171                 case 1 :
34172                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34173                     break;
34174                 case 2 :
34175                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34176                     break;
34177                 case 3 :
34178                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34179                     break;
34180                 case 4 :
34181                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34182                     break;
34183                 default :
34184                     break;
34185             }
34186             
34187             Roo.each(box, function(b,kk){
34188                 
34189                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34190                 
34191                 var sz = b.el.getSize();
34192                 
34193                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34194                 
34195             }, this);
34196             
34197         }, this);
34198         
34199         var mY = 0;
34200         
34201         for (var i = 0; i < this.cols; i++){
34202             mY = Math.max(mY, maxY[i]);
34203         }
34204         
34205         this.el.setHeight(mY - pos.y);
34206         
34207     },
34208     
34209 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34210 //    {
34211 //        var pos = this.el.getBox(true);
34212 //        var x = pos.x;
34213 //        var y = pos.y;
34214 //        var maxX = pos.right;
34215 //        
34216 //        var maxHeight = 0;
34217 //        
34218 //        Roo.each(items, function(item, k){
34219 //            
34220 //            var c = k % 2;
34221 //            
34222 //            item.el.position('absolute');
34223 //                
34224 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34225 //
34226 //            item.el.setWidth(width);
34227 //
34228 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34229 //
34230 //            item.el.setHeight(height);
34231 //            
34232 //            if(c == 0){
34233 //                item.el.setXY([x, y], isInstant ? false : true);
34234 //            } else {
34235 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34236 //            }
34237 //            
34238 //            y = y + height + this.alternativePadWidth;
34239 //            
34240 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34241 //            
34242 //        }, this);
34243 //        
34244 //        this.el.setHeight(maxHeight);
34245 //        
34246 //    },
34247     
34248     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34249     {
34250         var pos = this.el.getBox(true);
34251         
34252         var minX = pos.x;
34253         var minY = pos.y;
34254         
34255         var maxX = pos.right;
34256         
34257         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34258         
34259         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34260         
34261         Roo.each(queue, function(box, k){
34262             
34263             Roo.each(box, function(b, kk){
34264                 
34265                 b.el.position('absolute');
34266                 
34267                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34268                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34269                 
34270                 if(b.size == 'md-left' || b.size == 'md-right'){
34271                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34272                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34273                 }
34274                 
34275                 b.el.setWidth(width);
34276                 b.el.setHeight(height);
34277                 
34278             }, this);
34279             
34280             if(!box.length){
34281                 return;
34282             }
34283             
34284             var positions = [];
34285             
34286             switch (box.length){
34287                 case 1 :
34288                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34289                     break;
34290                 case 2 :
34291                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34292                     break;
34293                 case 3 :
34294                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34295                     break;
34296                 case 4 :
34297                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34298                     break;
34299                 default :
34300                     break;
34301             }
34302             
34303             Roo.each(box, function(b,kk){
34304                 
34305                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34306                 
34307                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34308                 
34309             }, this);
34310             
34311         }, this);
34312         
34313     },
34314     
34315     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34316     {
34317         Roo.each(eItems, function(b,k){
34318             
34319             b.size = (k == 0) ? 'sm' : 'xs';
34320             b.x = (k == 0) ? 2 : 1;
34321             b.y = (k == 0) ? 2 : 1;
34322             
34323             b.el.position('absolute');
34324             
34325             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34326                 
34327             b.el.setWidth(width);
34328             
34329             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34330             
34331             b.el.setHeight(height);
34332             
34333         }, this);
34334
34335         var positions = [];
34336         
34337         positions.push({
34338             x : maxX - this.unitWidth * 2 - this.gutter,
34339             y : minY
34340         });
34341         
34342         positions.push({
34343             x : maxX - this.unitWidth,
34344             y : minY + (this.unitWidth + this.gutter) * 2
34345         });
34346         
34347         positions.push({
34348             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34349             y : minY
34350         });
34351         
34352         Roo.each(eItems, function(b,k){
34353             
34354             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34355
34356         }, this);
34357         
34358     },
34359     
34360     getVerticalOneBoxColPositions : function(x, y, box)
34361     {
34362         var pos = [];
34363         
34364         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34365         
34366         if(box[0].size == 'md-left'){
34367             rand = 0;
34368         }
34369         
34370         if(box[0].size == 'md-right'){
34371             rand = 1;
34372         }
34373         
34374         pos.push({
34375             x : x + (this.unitWidth + this.gutter) * rand,
34376             y : y
34377         });
34378         
34379         return pos;
34380     },
34381     
34382     getVerticalTwoBoxColPositions : function(x, y, box)
34383     {
34384         var pos = [];
34385         
34386         if(box[0].size == 'xs'){
34387             
34388             pos.push({
34389                 x : x,
34390                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34391             });
34392
34393             pos.push({
34394                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34395                 y : y
34396             });
34397             
34398             return pos;
34399             
34400         }
34401         
34402         pos.push({
34403             x : x,
34404             y : y
34405         });
34406
34407         pos.push({
34408             x : x + (this.unitWidth + this.gutter) * 2,
34409             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34410         });
34411         
34412         return pos;
34413         
34414     },
34415     
34416     getVerticalThreeBoxColPositions : function(x, y, box)
34417     {
34418         var pos = [];
34419         
34420         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34421             
34422             pos.push({
34423                 x : x,
34424                 y : y
34425             });
34426
34427             pos.push({
34428                 x : x + (this.unitWidth + this.gutter) * 1,
34429                 y : y
34430             });
34431             
34432             pos.push({
34433                 x : x + (this.unitWidth + this.gutter) * 2,
34434                 y : y
34435             });
34436             
34437             return pos;
34438             
34439         }
34440         
34441         if(box[0].size == 'xs' && box[1].size == 'xs'){
34442             
34443             pos.push({
34444                 x : x,
34445                 y : y
34446             });
34447
34448             pos.push({
34449                 x : x,
34450                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34451             });
34452             
34453             pos.push({
34454                 x : x + (this.unitWidth + this.gutter) * 1,
34455                 y : y
34456             });
34457             
34458             return pos;
34459             
34460         }
34461         
34462         pos.push({
34463             x : x,
34464             y : y
34465         });
34466
34467         pos.push({
34468             x : x + (this.unitWidth + this.gutter) * 2,
34469             y : y
34470         });
34471
34472         pos.push({
34473             x : x + (this.unitWidth + this.gutter) * 2,
34474             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34475         });
34476             
34477         return pos;
34478         
34479     },
34480     
34481     getVerticalFourBoxColPositions : function(x, y, box)
34482     {
34483         var pos = [];
34484         
34485         if(box[0].size == 'xs'){
34486             
34487             pos.push({
34488                 x : x,
34489                 y : y
34490             });
34491
34492             pos.push({
34493                 x : x,
34494                 y : y + (this.unitHeight + this.gutter) * 1
34495             });
34496             
34497             pos.push({
34498                 x : x,
34499                 y : y + (this.unitHeight + this.gutter) * 2
34500             });
34501             
34502             pos.push({
34503                 x : x + (this.unitWidth + this.gutter) * 1,
34504                 y : y
34505             });
34506             
34507             return pos;
34508             
34509         }
34510         
34511         pos.push({
34512             x : x,
34513             y : y
34514         });
34515
34516         pos.push({
34517             x : x + (this.unitWidth + this.gutter) * 2,
34518             y : y
34519         });
34520
34521         pos.push({
34522             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34523             y : y + (this.unitHeight + this.gutter) * 1
34524         });
34525
34526         pos.push({
34527             x : x + (this.unitWidth + this.gutter) * 2,
34528             y : y + (this.unitWidth + this.gutter) * 2
34529         });
34530
34531         return pos;
34532         
34533     },
34534     
34535     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34536     {
34537         var pos = [];
34538         
34539         if(box[0].size == 'md-left'){
34540             pos.push({
34541                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34542                 y : minY
34543             });
34544             
34545             return pos;
34546         }
34547         
34548         if(box[0].size == 'md-right'){
34549             pos.push({
34550                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34551                 y : minY + (this.unitWidth + this.gutter) * 1
34552             });
34553             
34554             return pos;
34555         }
34556         
34557         var rand = Math.floor(Math.random() * (4 - box[0].y));
34558         
34559         pos.push({
34560             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34561             y : minY + (this.unitWidth + this.gutter) * rand
34562         });
34563         
34564         return pos;
34565         
34566     },
34567     
34568     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34569     {
34570         var pos = [];
34571         
34572         if(box[0].size == 'xs'){
34573             
34574             pos.push({
34575                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34576                 y : minY
34577             });
34578
34579             pos.push({
34580                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34581                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34582             });
34583             
34584             return pos;
34585             
34586         }
34587         
34588         pos.push({
34589             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34590             y : minY
34591         });
34592
34593         pos.push({
34594             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34595             y : minY + (this.unitWidth + this.gutter) * 2
34596         });
34597         
34598         return pos;
34599         
34600     },
34601     
34602     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34603     {
34604         var pos = [];
34605         
34606         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34607             
34608             pos.push({
34609                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34610                 y : minY
34611             });
34612
34613             pos.push({
34614                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34615                 y : minY + (this.unitWidth + this.gutter) * 1
34616             });
34617             
34618             pos.push({
34619                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34620                 y : minY + (this.unitWidth + this.gutter) * 2
34621             });
34622             
34623             return pos;
34624             
34625         }
34626         
34627         if(box[0].size == 'xs' && box[1].size == 'xs'){
34628             
34629             pos.push({
34630                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34631                 y : minY
34632             });
34633
34634             pos.push({
34635                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34636                 y : minY
34637             });
34638             
34639             pos.push({
34640                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34641                 y : minY + (this.unitWidth + this.gutter) * 1
34642             });
34643             
34644             return pos;
34645             
34646         }
34647         
34648         pos.push({
34649             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34650             y : minY
34651         });
34652
34653         pos.push({
34654             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34655             y : minY + (this.unitWidth + this.gutter) * 2
34656         });
34657
34658         pos.push({
34659             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34660             y : minY + (this.unitWidth + this.gutter) * 2
34661         });
34662             
34663         return pos;
34664         
34665     },
34666     
34667     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34668     {
34669         var pos = [];
34670         
34671         if(box[0].size == 'xs'){
34672             
34673             pos.push({
34674                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34675                 y : minY
34676             });
34677
34678             pos.push({
34679                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34680                 y : minY
34681             });
34682             
34683             pos.push({
34684                 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),
34685                 y : minY
34686             });
34687             
34688             pos.push({
34689                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34690                 y : minY + (this.unitWidth + this.gutter) * 1
34691             });
34692             
34693             return pos;
34694             
34695         }
34696         
34697         pos.push({
34698             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34699             y : minY
34700         });
34701         
34702         pos.push({
34703             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34704             y : minY + (this.unitWidth + this.gutter) * 2
34705         });
34706         
34707         pos.push({
34708             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34709             y : minY + (this.unitWidth + this.gutter) * 2
34710         });
34711         
34712         pos.push({
34713             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),
34714             y : minY + (this.unitWidth + this.gutter) * 2
34715         });
34716
34717         return pos;
34718         
34719     },
34720     
34721     /**
34722     * remove a Masonry Brick
34723     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34724     */
34725     removeBrick : function(brick_id)
34726     {
34727         if (!brick_id) {
34728             return;
34729         }
34730         
34731         for (var i = 0; i<this.bricks.length; i++) {
34732             if (this.bricks[i].id == brick_id) {
34733                 this.bricks.splice(i,1);
34734                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34735                 this.initial();
34736             }
34737         }
34738     },
34739     
34740     /**
34741     * adds a Masonry Brick
34742     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34743     */
34744     addBrick : function(cfg)
34745     {
34746         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34747         //this.register(cn);
34748         cn.parentId = this.id;
34749         cn.render(this.el);
34750         return cn;
34751     },
34752     
34753     /**
34754     * register a Masonry Brick
34755     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34756     */
34757     
34758     register : function(brick)
34759     {
34760         this.bricks.push(brick);
34761         brick.masonryId = this.id;
34762     },
34763     
34764     /**
34765     * clear all the Masonry Brick
34766     */
34767     clearAll : function()
34768     {
34769         this.bricks = [];
34770         //this.getChildContainer().dom.innerHTML = "";
34771         this.el.dom.innerHTML = '';
34772     },
34773     
34774     getSelected : function()
34775     {
34776         if (!this.selectedBrick) {
34777             return false;
34778         }
34779         
34780         return this.selectedBrick;
34781     }
34782 });
34783
34784 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34785     
34786     groups: {},
34787      /**
34788     * register a Masonry Layout
34789     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34790     */
34791     
34792     register : function(layout)
34793     {
34794         this.groups[layout.id] = layout;
34795     },
34796     /**
34797     * fetch a  Masonry Layout based on the masonry layout ID
34798     * @param {string} the masonry layout to add
34799     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34800     */
34801     
34802     get: function(layout_id) {
34803         if (typeof(this.groups[layout_id]) == 'undefined') {
34804             return false;
34805         }
34806         return this.groups[layout_id] ;
34807     }
34808     
34809     
34810     
34811 });
34812
34813  
34814
34815  /**
34816  *
34817  * This is based on 
34818  * http://masonry.desandro.com
34819  *
34820  * The idea is to render all the bricks based on vertical width...
34821  *
34822  * The original code extends 'outlayer' - we might need to use that....
34823  * 
34824  */
34825
34826
34827 /**
34828  * @class Roo.bootstrap.LayoutMasonryAuto
34829  * @extends Roo.bootstrap.Component
34830  * Bootstrap Layout Masonry class
34831  * 
34832  * @constructor
34833  * Create a new Element
34834  * @param {Object} config The config object
34835  */
34836
34837 Roo.bootstrap.LayoutMasonryAuto = function(config){
34838     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34839 };
34840
34841 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34842     
34843       /**
34844      * @cfg {Boolean} isFitWidth  - resize the width..
34845      */   
34846     isFitWidth : false,  // options..
34847     /**
34848      * @cfg {Boolean} isOriginLeft = left align?
34849      */   
34850     isOriginLeft : true,
34851     /**
34852      * @cfg {Boolean} isOriginTop = top align?
34853      */   
34854     isOriginTop : false,
34855     /**
34856      * @cfg {Boolean} isLayoutInstant = no animation?
34857      */   
34858     isLayoutInstant : false, // needed?
34859     /**
34860      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34861      */   
34862     isResizingContainer : true,
34863     /**
34864      * @cfg {Number} columnWidth  width of the columns 
34865      */   
34866     
34867     columnWidth : 0,
34868     
34869     /**
34870      * @cfg {Number} maxCols maximum number of columns
34871      */   
34872     
34873     maxCols: 0,
34874     /**
34875      * @cfg {Number} padHeight padding below box..
34876      */   
34877     
34878     padHeight : 10, 
34879     
34880     /**
34881      * @cfg {Boolean} isAutoInitial defalut true
34882      */   
34883     
34884     isAutoInitial : true, 
34885     
34886     // private?
34887     gutter : 0,
34888     
34889     containerWidth: 0,
34890     initialColumnWidth : 0,
34891     currentSize : null,
34892     
34893     colYs : null, // array.
34894     maxY : 0,
34895     padWidth: 10,
34896     
34897     
34898     tag: 'div',
34899     cls: '',
34900     bricks: null, //CompositeElement
34901     cols : 0, // array?
34902     // element : null, // wrapped now this.el
34903     _isLayoutInited : null, 
34904     
34905     
34906     getAutoCreate : function(){
34907         
34908         var cfg = {
34909             tag: this.tag,
34910             cls: 'blog-masonary-wrapper ' + this.cls,
34911             cn : {
34912                 cls : 'mas-boxes masonary'
34913             }
34914         };
34915         
34916         return cfg;
34917     },
34918     
34919     getChildContainer: function( )
34920     {
34921         if (this.boxesEl) {
34922             return this.boxesEl;
34923         }
34924         
34925         this.boxesEl = this.el.select('.mas-boxes').first();
34926         
34927         return this.boxesEl;
34928     },
34929     
34930     
34931     initEvents : function()
34932     {
34933         var _this = this;
34934         
34935         if(this.isAutoInitial){
34936             Roo.log('hook children rendered');
34937             this.on('childrenrendered', function() {
34938                 Roo.log('children rendered');
34939                 _this.initial();
34940             } ,this);
34941         }
34942         
34943     },
34944     
34945     initial : function()
34946     {
34947         this.reloadItems();
34948
34949         this.currentSize = this.el.getBox(true);
34950
34951         /// was window resize... - let's see if this works..
34952         Roo.EventManager.onWindowResize(this.resize, this); 
34953
34954         if(!this.isAutoInitial){
34955             this.layout();
34956             return;
34957         }
34958         
34959         this.layout.defer(500,this);
34960     },
34961     
34962     reloadItems: function()
34963     {
34964         this.bricks = this.el.select('.masonry-brick', true);
34965         
34966         this.bricks.each(function(b) {
34967             //Roo.log(b.getSize());
34968             if (!b.attr('originalwidth')) {
34969                 b.attr('originalwidth',  b.getSize().width);
34970             }
34971             
34972         });
34973         
34974         Roo.log(this.bricks.elements.length);
34975     },
34976     
34977     resize : function()
34978     {
34979         Roo.log('resize');
34980         var cs = this.el.getBox(true);
34981         
34982         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34983             Roo.log("no change in with or X");
34984             return;
34985         }
34986         this.currentSize = cs;
34987         this.layout();
34988     },
34989     
34990     layout : function()
34991     {
34992          Roo.log('layout');
34993         this._resetLayout();
34994         //this._manageStamps();
34995       
34996         // don't animate first layout
34997         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34998         this.layoutItems( isInstant );
34999       
35000         // flag for initalized
35001         this._isLayoutInited = true;
35002     },
35003     
35004     layoutItems : function( isInstant )
35005     {
35006         //var items = this._getItemsForLayout( this.items );
35007         // original code supports filtering layout items.. we just ignore it..
35008         
35009         this._layoutItems( this.bricks , isInstant );
35010       
35011         this._postLayout();
35012     },
35013     _layoutItems : function ( items , isInstant)
35014     {
35015        //this.fireEvent( 'layout', this, items );
35016     
35017
35018         if ( !items || !items.elements.length ) {
35019           // no items, emit event with empty array
35020             return;
35021         }
35022
35023         var queue = [];
35024         items.each(function(item) {
35025             Roo.log("layout item");
35026             Roo.log(item);
35027             // get x/y object from method
35028             var position = this._getItemLayoutPosition( item );
35029             // enqueue
35030             position.item = item;
35031             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35032             queue.push( position );
35033         }, this);
35034       
35035         this._processLayoutQueue( queue );
35036     },
35037     /** Sets position of item in DOM
35038     * @param {Element} item
35039     * @param {Number} x - horizontal position
35040     * @param {Number} y - vertical position
35041     * @param {Boolean} isInstant - disables transitions
35042     */
35043     _processLayoutQueue : function( queue )
35044     {
35045         for ( var i=0, len = queue.length; i < len; i++ ) {
35046             var obj = queue[i];
35047             obj.item.position('absolute');
35048             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35049         }
35050     },
35051       
35052     
35053     /**
35054     * Any logic you want to do after each layout,
35055     * i.e. size the container
35056     */
35057     _postLayout : function()
35058     {
35059         this.resizeContainer();
35060     },
35061     
35062     resizeContainer : function()
35063     {
35064         if ( !this.isResizingContainer ) {
35065             return;
35066         }
35067         var size = this._getContainerSize();
35068         if ( size ) {
35069             this.el.setSize(size.width,size.height);
35070             this.boxesEl.setSize(size.width,size.height);
35071         }
35072     },
35073     
35074     
35075     
35076     _resetLayout : function()
35077     {
35078         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35079         this.colWidth = this.el.getWidth();
35080         //this.gutter = this.el.getWidth(); 
35081         
35082         this.measureColumns();
35083
35084         // reset column Y
35085         var i = this.cols;
35086         this.colYs = [];
35087         while (i--) {
35088             this.colYs.push( 0 );
35089         }
35090     
35091         this.maxY = 0;
35092     },
35093
35094     measureColumns : function()
35095     {
35096         this.getContainerWidth();
35097       // if columnWidth is 0, default to outerWidth of first item
35098         if ( !this.columnWidth ) {
35099             var firstItem = this.bricks.first();
35100             Roo.log(firstItem);
35101             this.columnWidth  = this.containerWidth;
35102             if (firstItem && firstItem.attr('originalwidth') ) {
35103                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35104             }
35105             // columnWidth fall back to item of first element
35106             Roo.log("set column width?");
35107                         this.initialColumnWidth = this.columnWidth  ;
35108
35109             // if first elem has no width, default to size of container
35110             
35111         }
35112         
35113         
35114         if (this.initialColumnWidth) {
35115             this.columnWidth = this.initialColumnWidth;
35116         }
35117         
35118         
35119             
35120         // column width is fixed at the top - however if container width get's smaller we should
35121         // reduce it...
35122         
35123         // this bit calcs how man columns..
35124             
35125         var columnWidth = this.columnWidth += this.gutter;
35126       
35127         // calculate columns
35128         var containerWidth = this.containerWidth + this.gutter;
35129         
35130         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35131         // fix rounding errors, typically with gutters
35132         var excess = columnWidth - containerWidth % columnWidth;
35133         
35134         
35135         // if overshoot is less than a pixel, round up, otherwise floor it
35136         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35137         cols = Math[ mathMethod ]( cols );
35138         this.cols = Math.max( cols, 1 );
35139         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35140         
35141          // padding positioning..
35142         var totalColWidth = this.cols * this.columnWidth;
35143         var padavail = this.containerWidth - totalColWidth;
35144         // so for 2 columns - we need 3 'pads'
35145         
35146         var padNeeded = (1+this.cols) * this.padWidth;
35147         
35148         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35149         
35150         this.columnWidth += padExtra
35151         //this.padWidth = Math.floor(padavail /  ( this.cols));
35152         
35153         // adjust colum width so that padding is fixed??
35154         
35155         // we have 3 columns ... total = width * 3
35156         // we have X left over... that should be used by 
35157         
35158         //if (this.expandC) {
35159             
35160         //}
35161         
35162         
35163         
35164     },
35165     
35166     getContainerWidth : function()
35167     {
35168        /* // container is parent if fit width
35169         var container = this.isFitWidth ? this.element.parentNode : this.element;
35170         // check that this.size and size are there
35171         // IE8 triggers resize on body size change, so they might not be
35172         
35173         var size = getSize( container );  //FIXME
35174         this.containerWidth = size && size.innerWidth; //FIXME
35175         */
35176          
35177         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35178         
35179     },
35180     
35181     _getItemLayoutPosition : function( item )  // what is item?
35182     {
35183         // we resize the item to our columnWidth..
35184       
35185         item.setWidth(this.columnWidth);
35186         item.autoBoxAdjust  = false;
35187         
35188         var sz = item.getSize();
35189  
35190         // how many columns does this brick span
35191         var remainder = this.containerWidth % this.columnWidth;
35192         
35193         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35194         // round if off by 1 pixel, otherwise use ceil
35195         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35196         colSpan = Math.min( colSpan, this.cols );
35197         
35198         // normally this should be '1' as we dont' currently allow multi width columns..
35199         
35200         var colGroup = this._getColGroup( colSpan );
35201         // get the minimum Y value from the columns
35202         var minimumY = Math.min.apply( Math, colGroup );
35203         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35204         
35205         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35206          
35207         // position the brick
35208         var position = {
35209             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35210             y: this.currentSize.y + minimumY + this.padHeight
35211         };
35212         
35213         Roo.log(position);
35214         // apply setHeight to necessary columns
35215         var setHeight = minimumY + sz.height + this.padHeight;
35216         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35217         
35218         var setSpan = this.cols + 1 - colGroup.length;
35219         for ( var i = 0; i < setSpan; i++ ) {
35220           this.colYs[ shortColIndex + i ] = setHeight ;
35221         }
35222       
35223         return position;
35224     },
35225     
35226     /**
35227      * @param {Number} colSpan - number of columns the element spans
35228      * @returns {Array} colGroup
35229      */
35230     _getColGroup : function( colSpan )
35231     {
35232         if ( colSpan < 2 ) {
35233           // if brick spans only one column, use all the column Ys
35234           return this.colYs;
35235         }
35236       
35237         var colGroup = [];
35238         // how many different places could this brick fit horizontally
35239         var groupCount = this.cols + 1 - colSpan;
35240         // for each group potential horizontal position
35241         for ( var i = 0; i < groupCount; i++ ) {
35242           // make an array of colY values for that one group
35243           var groupColYs = this.colYs.slice( i, i + colSpan );
35244           // and get the max value of the array
35245           colGroup[i] = Math.max.apply( Math, groupColYs );
35246         }
35247         return colGroup;
35248     },
35249     /*
35250     _manageStamp : function( stamp )
35251     {
35252         var stampSize =  stamp.getSize();
35253         var offset = stamp.getBox();
35254         // get the columns that this stamp affects
35255         var firstX = this.isOriginLeft ? offset.x : offset.right;
35256         var lastX = firstX + stampSize.width;
35257         var firstCol = Math.floor( firstX / this.columnWidth );
35258         firstCol = Math.max( 0, firstCol );
35259         
35260         var lastCol = Math.floor( lastX / this.columnWidth );
35261         // lastCol should not go over if multiple of columnWidth #425
35262         lastCol -= lastX % this.columnWidth ? 0 : 1;
35263         lastCol = Math.min( this.cols - 1, lastCol );
35264         
35265         // set colYs to bottom of the stamp
35266         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35267             stampSize.height;
35268             
35269         for ( var i = firstCol; i <= lastCol; i++ ) {
35270           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35271         }
35272     },
35273     */
35274     
35275     _getContainerSize : function()
35276     {
35277         this.maxY = Math.max.apply( Math, this.colYs );
35278         var size = {
35279             height: this.maxY
35280         };
35281       
35282         if ( this.isFitWidth ) {
35283             size.width = this._getContainerFitWidth();
35284         }
35285       
35286         return size;
35287     },
35288     
35289     _getContainerFitWidth : function()
35290     {
35291         var unusedCols = 0;
35292         // count unused columns
35293         var i = this.cols;
35294         while ( --i ) {
35295           if ( this.colYs[i] !== 0 ) {
35296             break;
35297           }
35298           unusedCols++;
35299         }
35300         // fit container to columns that have been used
35301         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35302     },
35303     
35304     needsResizeLayout : function()
35305     {
35306         var previousWidth = this.containerWidth;
35307         this.getContainerWidth();
35308         return previousWidth !== this.containerWidth;
35309     }
35310  
35311 });
35312
35313  
35314
35315  /*
35316  * - LGPL
35317  *
35318  * element
35319  * 
35320  */
35321
35322 /**
35323  * @class Roo.bootstrap.MasonryBrick
35324  * @extends Roo.bootstrap.Component
35325  * Bootstrap MasonryBrick class
35326  * 
35327  * @constructor
35328  * Create a new MasonryBrick
35329  * @param {Object} config The config object
35330  */
35331
35332 Roo.bootstrap.MasonryBrick = function(config){
35333     
35334     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35335     
35336     Roo.bootstrap.MasonryBrick.register(this);
35337     
35338     this.addEvents({
35339         // raw events
35340         /**
35341          * @event click
35342          * When a MasonryBrick is clcik
35343          * @param {Roo.bootstrap.MasonryBrick} this
35344          * @param {Roo.EventObject} e
35345          */
35346         "click" : true
35347     });
35348 };
35349
35350 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35351     
35352     /**
35353      * @cfg {String} title
35354      */   
35355     title : '',
35356     /**
35357      * @cfg {String} html
35358      */   
35359     html : '',
35360     /**
35361      * @cfg {String} bgimage
35362      */   
35363     bgimage : '',
35364     /**
35365      * @cfg {String} videourl
35366      */   
35367     videourl : '',
35368     /**
35369      * @cfg {String} cls
35370      */   
35371     cls : '',
35372     /**
35373      * @cfg {String} href
35374      */   
35375     href : '',
35376     /**
35377      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35378      */   
35379     size : 'xs',
35380     
35381     /**
35382      * @cfg {String} placetitle (center|bottom)
35383      */   
35384     placetitle : '',
35385     
35386     /**
35387      * @cfg {Boolean} isFitContainer defalut true
35388      */   
35389     isFitContainer : true, 
35390     
35391     /**
35392      * @cfg {Boolean} preventDefault defalut false
35393      */   
35394     preventDefault : false, 
35395     
35396     /**
35397      * @cfg {Boolean} inverse defalut false
35398      */   
35399     maskInverse : false, 
35400     
35401     getAutoCreate : function()
35402     {
35403         if(!this.isFitContainer){
35404             return this.getSplitAutoCreate();
35405         }
35406         
35407         var cls = 'masonry-brick masonry-brick-full';
35408         
35409         if(this.href.length){
35410             cls += ' masonry-brick-link';
35411         }
35412         
35413         if(this.bgimage.length){
35414             cls += ' masonry-brick-image';
35415         }
35416         
35417         if(this.maskInverse){
35418             cls += ' mask-inverse';
35419         }
35420         
35421         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35422             cls += ' enable-mask';
35423         }
35424         
35425         if(this.size){
35426             cls += ' masonry-' + this.size + '-brick';
35427         }
35428         
35429         if(this.placetitle.length){
35430             
35431             switch (this.placetitle) {
35432                 case 'center' :
35433                     cls += ' masonry-center-title';
35434                     break;
35435                 case 'bottom' :
35436                     cls += ' masonry-bottom-title';
35437                     break;
35438                 default:
35439                     break;
35440             }
35441             
35442         } else {
35443             if(!this.html.length && !this.bgimage.length){
35444                 cls += ' masonry-center-title';
35445             }
35446
35447             if(!this.html.length && this.bgimage.length){
35448                 cls += ' masonry-bottom-title';
35449             }
35450         }
35451         
35452         if(this.cls){
35453             cls += ' ' + this.cls;
35454         }
35455         
35456         var cfg = {
35457             tag: (this.href.length) ? 'a' : 'div',
35458             cls: cls,
35459             cn: [
35460                 {
35461                     tag: 'div',
35462                     cls: 'masonry-brick-mask'
35463                 },
35464                 {
35465                     tag: 'div',
35466                     cls: 'masonry-brick-paragraph',
35467                     cn: []
35468                 }
35469             ]
35470         };
35471         
35472         if(this.href.length){
35473             cfg.href = this.href;
35474         }
35475         
35476         var cn = cfg.cn[1].cn;
35477         
35478         if(this.title.length){
35479             cn.push({
35480                 tag: 'h4',
35481                 cls: 'masonry-brick-title',
35482                 html: this.title
35483             });
35484         }
35485         
35486         if(this.html.length){
35487             cn.push({
35488                 tag: 'p',
35489                 cls: 'masonry-brick-text',
35490                 html: this.html
35491             });
35492         }
35493         
35494         if (!this.title.length && !this.html.length) {
35495             cfg.cn[1].cls += ' hide';
35496         }
35497         
35498         if(this.bgimage.length){
35499             cfg.cn.push({
35500                 tag: 'img',
35501                 cls: 'masonry-brick-image-view',
35502                 src: this.bgimage
35503             });
35504         }
35505         
35506         if(this.videourl.length){
35507             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35508             // youtube support only?
35509             cfg.cn.push({
35510                 tag: 'iframe',
35511                 cls: 'masonry-brick-image-view',
35512                 src: vurl,
35513                 frameborder : 0,
35514                 allowfullscreen : true
35515             });
35516         }
35517         
35518         return cfg;
35519         
35520     },
35521     
35522     getSplitAutoCreate : function()
35523     {
35524         var cls = 'masonry-brick masonry-brick-split';
35525         
35526         if(this.href.length){
35527             cls += ' masonry-brick-link';
35528         }
35529         
35530         if(this.bgimage.length){
35531             cls += ' masonry-brick-image';
35532         }
35533         
35534         if(this.size){
35535             cls += ' masonry-' + this.size + '-brick';
35536         }
35537         
35538         switch (this.placetitle) {
35539             case 'center' :
35540                 cls += ' masonry-center-title';
35541                 break;
35542             case 'bottom' :
35543                 cls += ' masonry-bottom-title';
35544                 break;
35545             default:
35546                 if(!this.bgimage.length){
35547                     cls += ' masonry-center-title';
35548                 }
35549
35550                 if(this.bgimage.length){
35551                     cls += ' masonry-bottom-title';
35552                 }
35553                 break;
35554         }
35555         
35556         if(this.cls){
35557             cls += ' ' + this.cls;
35558         }
35559         
35560         var cfg = {
35561             tag: (this.href.length) ? 'a' : 'div',
35562             cls: cls,
35563             cn: [
35564                 {
35565                     tag: 'div',
35566                     cls: 'masonry-brick-split-head',
35567                     cn: [
35568                         {
35569                             tag: 'div',
35570                             cls: 'masonry-brick-paragraph',
35571                             cn: []
35572                         }
35573                     ]
35574                 },
35575                 {
35576                     tag: 'div',
35577                     cls: 'masonry-brick-split-body',
35578                     cn: []
35579                 }
35580             ]
35581         };
35582         
35583         if(this.href.length){
35584             cfg.href = this.href;
35585         }
35586         
35587         if(this.title.length){
35588             cfg.cn[0].cn[0].cn.push({
35589                 tag: 'h4',
35590                 cls: 'masonry-brick-title',
35591                 html: this.title
35592             });
35593         }
35594         
35595         if(this.html.length){
35596             cfg.cn[1].cn.push({
35597                 tag: 'p',
35598                 cls: 'masonry-brick-text',
35599                 html: this.html
35600             });
35601         }
35602
35603         if(this.bgimage.length){
35604             cfg.cn[0].cn.push({
35605                 tag: 'img',
35606                 cls: 'masonry-brick-image-view',
35607                 src: this.bgimage
35608             });
35609         }
35610         
35611         if(this.videourl.length){
35612             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35613             // youtube support only?
35614             cfg.cn[0].cn.cn.push({
35615                 tag: 'iframe',
35616                 cls: 'masonry-brick-image-view',
35617                 src: vurl,
35618                 frameborder : 0,
35619                 allowfullscreen : true
35620             });
35621         }
35622         
35623         return cfg;
35624     },
35625     
35626     initEvents: function() 
35627     {
35628         switch (this.size) {
35629             case 'xs' :
35630                 this.x = 1;
35631                 this.y = 1;
35632                 break;
35633             case 'sm' :
35634                 this.x = 2;
35635                 this.y = 2;
35636                 break;
35637             case 'md' :
35638             case 'md-left' :
35639             case 'md-right' :
35640                 this.x = 3;
35641                 this.y = 3;
35642                 break;
35643             case 'tall' :
35644                 this.x = 2;
35645                 this.y = 3;
35646                 break;
35647             case 'wide' :
35648                 this.x = 3;
35649                 this.y = 2;
35650                 break;
35651             case 'wide-thin' :
35652                 this.x = 3;
35653                 this.y = 1;
35654                 break;
35655                         
35656             default :
35657                 break;
35658         }
35659         
35660         if(Roo.isTouch){
35661             this.el.on('touchstart', this.onTouchStart, this);
35662             this.el.on('touchmove', this.onTouchMove, this);
35663             this.el.on('touchend', this.onTouchEnd, this);
35664             this.el.on('contextmenu', this.onContextMenu, this);
35665         } else {
35666             this.el.on('mouseenter'  ,this.enter, this);
35667             this.el.on('mouseleave', this.leave, this);
35668             this.el.on('click', this.onClick, this);
35669         }
35670         
35671         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35672             this.parent().bricks.push(this);   
35673         }
35674         
35675     },
35676     
35677     onClick: function(e, el)
35678     {
35679         var time = this.endTimer - this.startTimer;
35680         // Roo.log(e.preventDefault());
35681         if(Roo.isTouch){
35682             if(time > 1000){
35683                 e.preventDefault();
35684                 return;
35685             }
35686         }
35687         
35688         if(!this.preventDefault){
35689             return;
35690         }
35691         
35692         e.preventDefault();
35693         
35694         if (this.activeClass != '') {
35695             this.selectBrick();
35696         }
35697         
35698         this.fireEvent('click', this, e);
35699     },
35700     
35701     enter: function(e, el)
35702     {
35703         e.preventDefault();
35704         
35705         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35706             return;
35707         }
35708         
35709         if(this.bgimage.length && this.html.length){
35710             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35711         }
35712     },
35713     
35714     leave: function(e, el)
35715     {
35716         e.preventDefault();
35717         
35718         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35719             return;
35720         }
35721         
35722         if(this.bgimage.length && this.html.length){
35723             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35724         }
35725     },
35726     
35727     onTouchStart: function(e, el)
35728     {
35729 //        e.preventDefault();
35730         
35731         this.touchmoved = false;
35732         
35733         if(!this.isFitContainer){
35734             return;
35735         }
35736         
35737         if(!this.bgimage.length || !this.html.length){
35738             return;
35739         }
35740         
35741         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35742         
35743         this.timer = new Date().getTime();
35744         
35745     },
35746     
35747     onTouchMove: function(e, el)
35748     {
35749         this.touchmoved = true;
35750     },
35751     
35752     onContextMenu : function(e,el)
35753     {
35754         e.preventDefault();
35755         e.stopPropagation();
35756         return false;
35757     },
35758     
35759     onTouchEnd: function(e, el)
35760     {
35761 //        e.preventDefault();
35762         
35763         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35764         
35765             this.leave(e,el);
35766             
35767             return;
35768         }
35769         
35770         if(!this.bgimage.length || !this.html.length){
35771             
35772             if(this.href.length){
35773                 window.location.href = this.href;
35774             }
35775             
35776             return;
35777         }
35778         
35779         if(!this.isFitContainer){
35780             return;
35781         }
35782         
35783         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35784         
35785         window.location.href = this.href;
35786     },
35787     
35788     //selection on single brick only
35789     selectBrick : function() {
35790         
35791         if (!this.parentId) {
35792             return;
35793         }
35794         
35795         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35796         var index = m.selectedBrick.indexOf(this.id);
35797         
35798         if ( index > -1) {
35799             m.selectedBrick.splice(index,1);
35800             this.el.removeClass(this.activeClass);
35801             return;
35802         }
35803         
35804         for(var i = 0; i < m.selectedBrick.length; i++) {
35805             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35806             b.el.removeClass(b.activeClass);
35807         }
35808         
35809         m.selectedBrick = [];
35810         
35811         m.selectedBrick.push(this.id);
35812         this.el.addClass(this.activeClass);
35813         return;
35814     },
35815     
35816     isSelected : function(){
35817         return this.el.hasClass(this.activeClass);
35818         
35819     }
35820 });
35821
35822 Roo.apply(Roo.bootstrap.MasonryBrick, {
35823     
35824     //groups: {},
35825     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35826      /**
35827     * register a Masonry Brick
35828     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35829     */
35830     
35831     register : function(brick)
35832     {
35833         //this.groups[brick.id] = brick;
35834         this.groups.add(brick.id, brick);
35835     },
35836     /**
35837     * fetch a  masonry brick based on the masonry brick ID
35838     * @param {string} the masonry brick to add
35839     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35840     */
35841     
35842     get: function(brick_id) 
35843     {
35844         // if (typeof(this.groups[brick_id]) == 'undefined') {
35845         //     return false;
35846         // }
35847         // return this.groups[brick_id] ;
35848         
35849         if(this.groups.key(brick_id)) {
35850             return this.groups.key(brick_id);
35851         }
35852         
35853         return false;
35854     }
35855     
35856     
35857     
35858 });
35859
35860  /*
35861  * - LGPL
35862  *
35863  * element
35864  * 
35865  */
35866
35867 /**
35868  * @class Roo.bootstrap.Brick
35869  * @extends Roo.bootstrap.Component
35870  * Bootstrap Brick class
35871  * 
35872  * @constructor
35873  * Create a new Brick
35874  * @param {Object} config The config object
35875  */
35876
35877 Roo.bootstrap.Brick = function(config){
35878     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35879     
35880     this.addEvents({
35881         // raw events
35882         /**
35883          * @event click
35884          * When a Brick is click
35885          * @param {Roo.bootstrap.Brick} this
35886          * @param {Roo.EventObject} e
35887          */
35888         "click" : true
35889     });
35890 };
35891
35892 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35893     
35894     /**
35895      * @cfg {String} title
35896      */   
35897     title : '',
35898     /**
35899      * @cfg {String} html
35900      */   
35901     html : '',
35902     /**
35903      * @cfg {String} bgimage
35904      */   
35905     bgimage : '',
35906     /**
35907      * @cfg {String} cls
35908      */   
35909     cls : '',
35910     /**
35911      * @cfg {String} href
35912      */   
35913     href : '',
35914     /**
35915      * @cfg {String} video
35916      */   
35917     video : '',
35918     /**
35919      * @cfg {Boolean} square
35920      */   
35921     square : true,
35922     
35923     getAutoCreate : function()
35924     {
35925         var cls = 'roo-brick';
35926         
35927         if(this.href.length){
35928             cls += ' roo-brick-link';
35929         }
35930         
35931         if(this.bgimage.length){
35932             cls += ' roo-brick-image';
35933         }
35934         
35935         if(!this.html.length && !this.bgimage.length){
35936             cls += ' roo-brick-center-title';
35937         }
35938         
35939         if(!this.html.length && this.bgimage.length){
35940             cls += ' roo-brick-bottom-title';
35941         }
35942         
35943         if(this.cls){
35944             cls += ' ' + this.cls;
35945         }
35946         
35947         var cfg = {
35948             tag: (this.href.length) ? 'a' : 'div',
35949             cls: cls,
35950             cn: [
35951                 {
35952                     tag: 'div',
35953                     cls: 'roo-brick-paragraph',
35954                     cn: []
35955                 }
35956             ]
35957         };
35958         
35959         if(this.href.length){
35960             cfg.href = this.href;
35961         }
35962         
35963         var cn = cfg.cn[0].cn;
35964         
35965         if(this.title.length){
35966             cn.push({
35967                 tag: 'h4',
35968                 cls: 'roo-brick-title',
35969                 html: this.title
35970             });
35971         }
35972         
35973         if(this.html.length){
35974             cn.push({
35975                 tag: 'p',
35976                 cls: 'roo-brick-text',
35977                 html: this.html
35978             });
35979         } else {
35980             cn.cls += ' hide';
35981         }
35982         
35983         if(this.bgimage.length){
35984             cfg.cn.push({
35985                 tag: 'img',
35986                 cls: 'roo-brick-image-view',
35987                 src: this.bgimage
35988             });
35989         }
35990         
35991         return cfg;
35992     },
35993     
35994     initEvents: function() 
35995     {
35996         if(this.title.length || this.html.length){
35997             this.el.on('mouseenter'  ,this.enter, this);
35998             this.el.on('mouseleave', this.leave, this);
35999         }
36000         
36001         Roo.EventManager.onWindowResize(this.resize, this); 
36002         
36003         if(this.bgimage.length){
36004             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36005             this.imageEl.on('load', this.onImageLoad, this);
36006             return;
36007         }
36008         
36009         this.resize();
36010     },
36011     
36012     onImageLoad : function()
36013     {
36014         this.resize();
36015     },
36016     
36017     resize : function()
36018     {
36019         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36020         
36021         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36022         
36023         if(this.bgimage.length){
36024             var image = this.el.select('.roo-brick-image-view', true).first();
36025             
36026             image.setWidth(paragraph.getWidth());
36027             
36028             if(this.square){
36029                 image.setHeight(paragraph.getWidth());
36030             }
36031             
36032             this.el.setHeight(image.getHeight());
36033             paragraph.setHeight(image.getHeight());
36034             
36035         }
36036         
36037     },
36038     
36039     enter: function(e, el)
36040     {
36041         e.preventDefault();
36042         
36043         if(this.bgimage.length){
36044             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36045             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36046         }
36047     },
36048     
36049     leave: function(e, el)
36050     {
36051         e.preventDefault();
36052         
36053         if(this.bgimage.length){
36054             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36055             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36056         }
36057     }
36058     
36059 });
36060
36061  
36062
36063  /*
36064  * - LGPL
36065  *
36066  * Number field 
36067  */
36068
36069 /**
36070  * @class Roo.bootstrap.NumberField
36071  * @extends Roo.bootstrap.Input
36072  * Bootstrap NumberField class
36073  * 
36074  * 
36075  * 
36076  * 
36077  * @constructor
36078  * Create a new NumberField
36079  * @param {Object} config The config object
36080  */
36081
36082 Roo.bootstrap.NumberField = function(config){
36083     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36084 };
36085
36086 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36087     
36088     /**
36089      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36090      */
36091     allowDecimals : true,
36092     /**
36093      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36094      */
36095     decimalSeparator : ".",
36096     /**
36097      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36098      */
36099     decimalPrecision : 2,
36100     /**
36101      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36102      */
36103     allowNegative : true,
36104     
36105     /**
36106      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36107      */
36108     allowZero: true,
36109     /**
36110      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36111      */
36112     minValue : Number.NEGATIVE_INFINITY,
36113     /**
36114      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36115      */
36116     maxValue : Number.MAX_VALUE,
36117     /**
36118      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36119      */
36120     minText : "The minimum value for this field is {0}",
36121     /**
36122      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36123      */
36124     maxText : "The maximum value for this field is {0}",
36125     /**
36126      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36127      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36128      */
36129     nanText : "{0} is not a valid number",
36130     /**
36131      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36132      */
36133     thousandsDelimiter : false,
36134     /**
36135      * @cfg {String} valueAlign alignment of value
36136      */
36137     valueAlign : "left",
36138
36139     getAutoCreate : function()
36140     {
36141         var hiddenInput = {
36142             tag: 'input',
36143             type: 'hidden',
36144             id: Roo.id(),
36145             cls: 'hidden-number-input'
36146         };
36147         
36148         if (this.name) {
36149             hiddenInput.name = this.name;
36150         }
36151         
36152         this.name = '';
36153         
36154         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36155         
36156         this.name = hiddenInput.name;
36157         
36158         if(cfg.cn.length > 0) {
36159             cfg.cn.push(hiddenInput);
36160         }
36161         
36162         return cfg;
36163     },
36164
36165     // private
36166     initEvents : function()
36167     {   
36168         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36169         
36170         var allowed = "0123456789";
36171         
36172         if(this.allowDecimals){
36173             allowed += this.decimalSeparator;
36174         }
36175         
36176         if(this.allowNegative){
36177             allowed += "-";
36178         }
36179         
36180         if(this.thousandsDelimiter) {
36181             allowed += ",";
36182         }
36183         
36184         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36185         
36186         var keyPress = function(e){
36187             
36188             var k = e.getKey();
36189             
36190             var c = e.getCharCode();
36191             
36192             if(
36193                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36194                     allowed.indexOf(String.fromCharCode(c)) === -1
36195             ){
36196                 e.stopEvent();
36197                 return;
36198             }
36199             
36200             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36201                 return;
36202             }
36203             
36204             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36205                 e.stopEvent();
36206             }
36207         };
36208         
36209         this.el.on("keypress", keyPress, this);
36210     },
36211     
36212     validateValue : function(value)
36213     {
36214         
36215         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36216             return false;
36217         }
36218         
36219         var num = this.parseValue(value);
36220         
36221         if(isNaN(num)){
36222             this.markInvalid(String.format(this.nanText, value));
36223             return false;
36224         }
36225         
36226         if(num < this.minValue){
36227             this.markInvalid(String.format(this.minText, this.minValue));
36228             return false;
36229         }
36230         
36231         if(num > this.maxValue){
36232             this.markInvalid(String.format(this.maxText, this.maxValue));
36233             return false;
36234         }
36235         
36236         return true;
36237     },
36238
36239     getValue : function()
36240     {
36241         var v = this.hiddenEl().getValue();
36242         
36243         return this.fixPrecision(this.parseValue(v));
36244     },
36245
36246     parseValue : function(value)
36247     {
36248         if(this.thousandsDelimiter) {
36249             value += "";
36250             r = new RegExp(",", "g");
36251             value = value.replace(r, "");
36252         }
36253         
36254         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36255         return isNaN(value) ? '' : value;
36256     },
36257
36258     fixPrecision : function(value)
36259     {
36260         if(this.thousandsDelimiter) {
36261             value += "";
36262             r = new RegExp(",", "g");
36263             value = value.replace(r, "");
36264         }
36265         
36266         var nan = isNaN(value);
36267         
36268         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36269             return nan ? '' : value;
36270         }
36271         return parseFloat(value).toFixed(this.decimalPrecision);
36272     },
36273
36274     setValue : function(v)
36275     {
36276         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36277         
36278         this.value = v;
36279         
36280         if(this.rendered){
36281             
36282             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36283             
36284             this.inputEl().dom.value = (v == '') ? '' :
36285                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36286             
36287             if(!this.allowZero && v === '0') {
36288                 this.hiddenEl().dom.value = '';
36289                 this.inputEl().dom.value = '';
36290             }
36291             
36292             this.validate();
36293         }
36294     },
36295
36296     decimalPrecisionFcn : function(v)
36297     {
36298         return Math.floor(v);
36299     },
36300
36301     beforeBlur : function()
36302     {
36303         var v = this.parseValue(this.getRawValue());
36304         
36305         if(v || v === 0 || v === ''){
36306             this.setValue(v);
36307         }
36308     },
36309     
36310     hiddenEl : function()
36311     {
36312         return this.el.select('input.hidden-number-input',true).first();
36313     }
36314     
36315 });
36316
36317  
36318
36319 /*
36320 * Licence: LGPL
36321 */
36322
36323 /**
36324  * @class Roo.bootstrap.DocumentSlider
36325  * @extends Roo.bootstrap.Component
36326  * Bootstrap DocumentSlider class
36327  * 
36328  * @constructor
36329  * Create a new DocumentViewer
36330  * @param {Object} config The config object
36331  */
36332
36333 Roo.bootstrap.DocumentSlider = function(config){
36334     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36335     
36336     this.files = [];
36337     
36338     this.addEvents({
36339         /**
36340          * @event initial
36341          * Fire after initEvent
36342          * @param {Roo.bootstrap.DocumentSlider} this
36343          */
36344         "initial" : true,
36345         /**
36346          * @event update
36347          * Fire after update
36348          * @param {Roo.bootstrap.DocumentSlider} this
36349          */
36350         "update" : true,
36351         /**
36352          * @event click
36353          * Fire after click
36354          * @param {Roo.bootstrap.DocumentSlider} this
36355          */
36356         "click" : true
36357     });
36358 };
36359
36360 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36361     
36362     files : false,
36363     
36364     indicator : 0,
36365     
36366     getAutoCreate : function()
36367     {
36368         var cfg = {
36369             tag : 'div',
36370             cls : 'roo-document-slider',
36371             cn : [
36372                 {
36373                     tag : 'div',
36374                     cls : 'roo-document-slider-header',
36375                     cn : [
36376                         {
36377                             tag : 'div',
36378                             cls : 'roo-document-slider-header-title'
36379                         }
36380                     ]
36381                 },
36382                 {
36383                     tag : 'div',
36384                     cls : 'roo-document-slider-body',
36385                     cn : [
36386                         {
36387                             tag : 'div',
36388                             cls : 'roo-document-slider-prev',
36389                             cn : [
36390                                 {
36391                                     tag : 'i',
36392                                     cls : 'fa fa-chevron-left'
36393                                 }
36394                             ]
36395                         },
36396                         {
36397                             tag : 'div',
36398                             cls : 'roo-document-slider-thumb',
36399                             cn : [
36400                                 {
36401                                     tag : 'img',
36402                                     cls : 'roo-document-slider-image'
36403                                 }
36404                             ]
36405                         },
36406                         {
36407                             tag : 'div',
36408                             cls : 'roo-document-slider-next',
36409                             cn : [
36410                                 {
36411                                     tag : 'i',
36412                                     cls : 'fa fa-chevron-right'
36413                                 }
36414                             ]
36415                         }
36416                     ]
36417                 }
36418             ]
36419         };
36420         
36421         return cfg;
36422     },
36423     
36424     initEvents : function()
36425     {
36426         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36427         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36428         
36429         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36430         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36431         
36432         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36433         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36434         
36435         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36436         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36437         
36438         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36439         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36440         
36441         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36442         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36443         
36444         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36445         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36446         
36447         this.thumbEl.on('click', this.onClick, this);
36448         
36449         this.prevIndicator.on('click', this.prev, this);
36450         
36451         this.nextIndicator.on('click', this.next, this);
36452         
36453     },
36454     
36455     initial : function()
36456     {
36457         if(this.files.length){
36458             this.indicator = 1;
36459             this.update()
36460         }
36461         
36462         this.fireEvent('initial', this);
36463     },
36464     
36465     update : function()
36466     {
36467         this.imageEl.attr('src', this.files[this.indicator - 1]);
36468         
36469         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36470         
36471         this.prevIndicator.show();
36472         
36473         if(this.indicator == 1){
36474             this.prevIndicator.hide();
36475         }
36476         
36477         this.nextIndicator.show();
36478         
36479         if(this.indicator == this.files.length){
36480             this.nextIndicator.hide();
36481         }
36482         
36483         this.thumbEl.scrollTo('top');
36484         
36485         this.fireEvent('update', this);
36486     },
36487     
36488     onClick : function(e)
36489     {
36490         e.preventDefault();
36491         
36492         this.fireEvent('click', this);
36493     },
36494     
36495     prev : function(e)
36496     {
36497         e.preventDefault();
36498         
36499         this.indicator = Math.max(1, this.indicator - 1);
36500         
36501         this.update();
36502     },
36503     
36504     next : function(e)
36505     {
36506         e.preventDefault();
36507         
36508         this.indicator = Math.min(this.files.length, this.indicator + 1);
36509         
36510         this.update();
36511     }
36512 });
36513 /*
36514  * - LGPL
36515  *
36516  * RadioSet
36517  *
36518  *
36519  */
36520
36521 /**
36522  * @class Roo.bootstrap.RadioSet
36523  * @extends Roo.bootstrap.Input
36524  * Bootstrap RadioSet class
36525  * @cfg {String} indicatorpos (left|right) default left
36526  * @cfg {Boolean} inline (true|false) inline the element (default true)
36527  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36528  * @constructor
36529  * Create a new RadioSet
36530  * @param {Object} config The config object
36531  */
36532
36533 Roo.bootstrap.RadioSet = function(config){
36534     
36535     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36536     
36537     this.radioes = [];
36538     
36539     Roo.bootstrap.RadioSet.register(this);
36540     
36541     this.addEvents({
36542         /**
36543         * @event check
36544         * Fires when the element is checked or unchecked.
36545         * @param {Roo.bootstrap.RadioSet} this This radio
36546         * @param {Roo.bootstrap.Radio} item The checked item
36547         */
36548        check : true,
36549        /**
36550         * @event click
36551         * Fires when the element is click.
36552         * @param {Roo.bootstrap.RadioSet} this This radio set
36553         * @param {Roo.bootstrap.Radio} item The checked item
36554         * @param {Roo.EventObject} e The event object
36555         */
36556        click : true
36557     });
36558     
36559 };
36560
36561 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36562
36563     radioes : false,
36564     
36565     inline : true,
36566     
36567     weight : '',
36568     
36569     indicatorpos : 'left',
36570     
36571     getAutoCreate : function()
36572     {
36573         var label = {
36574             tag : 'label',
36575             cls : 'roo-radio-set-label',
36576             cn : [
36577                 {
36578                     tag : 'span',
36579                     html : this.fieldLabel
36580                 }
36581             ]
36582         };
36583         if (Roo.bootstrap.version == 3) {
36584             
36585             
36586             if(this.indicatorpos == 'left'){
36587                 label.cn.unshift({
36588                     tag : 'i',
36589                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36590                     tooltip : 'This field is required'
36591                 });
36592             } else {
36593                 label.cn.push({
36594                     tag : 'i',
36595                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36596                     tooltip : 'This field is required'
36597                 });
36598             }
36599         }
36600         var items = {
36601             tag : 'div',
36602             cls : 'roo-radio-set-items'
36603         };
36604         
36605         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36606         
36607         if (align === 'left' && this.fieldLabel.length) {
36608             
36609             items = {
36610                 cls : "roo-radio-set-right", 
36611                 cn: [
36612                     items
36613                 ]
36614             };
36615             
36616             if(this.labelWidth > 12){
36617                 label.style = "width: " + this.labelWidth + 'px';
36618             }
36619             
36620             if(this.labelWidth < 13 && this.labelmd == 0){
36621                 this.labelmd = this.labelWidth;
36622             }
36623             
36624             if(this.labellg > 0){
36625                 label.cls += ' col-lg-' + this.labellg;
36626                 items.cls += ' col-lg-' + (12 - this.labellg);
36627             }
36628             
36629             if(this.labelmd > 0){
36630                 label.cls += ' col-md-' + this.labelmd;
36631                 items.cls += ' col-md-' + (12 - this.labelmd);
36632             }
36633             
36634             if(this.labelsm > 0){
36635                 label.cls += ' col-sm-' + this.labelsm;
36636                 items.cls += ' col-sm-' + (12 - this.labelsm);
36637             }
36638             
36639             if(this.labelxs > 0){
36640                 label.cls += ' col-xs-' + this.labelxs;
36641                 items.cls += ' col-xs-' + (12 - this.labelxs);
36642             }
36643         }
36644         
36645         var cfg = {
36646             tag : 'div',
36647             cls : 'roo-radio-set',
36648             cn : [
36649                 {
36650                     tag : 'input',
36651                     cls : 'roo-radio-set-input',
36652                     type : 'hidden',
36653                     name : this.name,
36654                     value : this.value ? this.value :  ''
36655                 },
36656                 label,
36657                 items
36658             ]
36659         };
36660         
36661         if(this.weight.length){
36662             cfg.cls += ' roo-radio-' + this.weight;
36663         }
36664         
36665         if(this.inline) {
36666             cfg.cls += ' roo-radio-set-inline';
36667         }
36668         
36669         var settings=this;
36670         ['xs','sm','md','lg'].map(function(size){
36671             if (settings[size]) {
36672                 cfg.cls += ' col-' + size + '-' + settings[size];
36673             }
36674         });
36675         
36676         return cfg;
36677         
36678     },
36679
36680     initEvents : function()
36681     {
36682         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36683         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36684         
36685         if(!this.fieldLabel.length){
36686             this.labelEl.hide();
36687         }
36688         
36689         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36690         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36691         
36692         this.indicator = this.indicatorEl();
36693         
36694         if(this.indicator){
36695             this.indicator.addClass('invisible');
36696         }
36697         
36698         this.originalValue = this.getValue();
36699         
36700     },
36701     
36702     inputEl: function ()
36703     {
36704         return this.el.select('.roo-radio-set-input', true).first();
36705     },
36706     
36707     getChildContainer : function()
36708     {
36709         return this.itemsEl;
36710     },
36711     
36712     register : function(item)
36713     {
36714         this.radioes.push(item);
36715         
36716     },
36717     
36718     validate : function()
36719     {   
36720         if(this.getVisibilityEl().hasClass('hidden')){
36721             return true;
36722         }
36723         
36724         var valid = false;
36725         
36726         Roo.each(this.radioes, function(i){
36727             if(!i.checked){
36728                 return;
36729             }
36730             
36731             valid = true;
36732             return false;
36733         });
36734         
36735         if(this.allowBlank) {
36736             return true;
36737         }
36738         
36739         if(this.disabled || valid){
36740             this.markValid();
36741             return true;
36742         }
36743         
36744         this.markInvalid();
36745         return false;
36746         
36747     },
36748     
36749     markValid : function()
36750     {
36751         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36752             this.indicatorEl().removeClass('visible');
36753             this.indicatorEl().addClass('invisible');
36754         }
36755         
36756         
36757         if (Roo.bootstrap.version == 3) {
36758             this.el.removeClass([this.invalidClass, this.validClass]);
36759             this.el.addClass(this.validClass);
36760         } else {
36761             this.el.removeClass(['is-invalid','is-valid']);
36762             this.el.addClass(['is-valid']);
36763         }
36764         this.fireEvent('valid', this);
36765     },
36766     
36767     markInvalid : function(msg)
36768     {
36769         if(this.allowBlank || this.disabled){
36770             return;
36771         }
36772         
36773         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36774             this.indicatorEl().removeClass('invisible');
36775             this.indicatorEl().addClass('visible');
36776         }
36777         if (Roo.bootstrap.version == 3) {
36778             this.el.removeClass([this.invalidClass, this.validClass]);
36779             this.el.addClass(this.invalidClass);
36780         } else {
36781             this.el.removeClass(['is-invalid','is-valid']);
36782             this.el.addClass(['is-invalid']);
36783         }
36784         
36785         this.fireEvent('invalid', this, msg);
36786         
36787     },
36788     
36789     setValue : function(v, suppressEvent)
36790     {   
36791         if(this.value === v){
36792             return;
36793         }
36794         
36795         this.value = v;
36796         
36797         if(this.rendered){
36798             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36799         }
36800         
36801         Roo.each(this.radioes, function(i){
36802             i.checked = false;
36803             i.el.removeClass('checked');
36804         });
36805         
36806         Roo.each(this.radioes, function(i){
36807             
36808             if(i.value === v || i.value.toString() === v.toString()){
36809                 i.checked = true;
36810                 i.el.addClass('checked');
36811                 
36812                 if(suppressEvent !== true){
36813                     this.fireEvent('check', this, i);
36814                 }
36815                 
36816                 return false;
36817             }
36818             
36819         }, this);
36820         
36821         this.validate();
36822     },
36823     
36824     clearInvalid : function(){
36825         
36826         if(!this.el || this.preventMark){
36827             return;
36828         }
36829         
36830         this.el.removeClass([this.invalidClass]);
36831         
36832         this.fireEvent('valid', this);
36833     }
36834     
36835 });
36836
36837 Roo.apply(Roo.bootstrap.RadioSet, {
36838     
36839     groups: {},
36840     
36841     register : function(set)
36842     {
36843         this.groups[set.name] = set;
36844     },
36845     
36846     get: function(name) 
36847     {
36848         if (typeof(this.groups[name]) == 'undefined') {
36849             return false;
36850         }
36851         
36852         return this.groups[name] ;
36853     }
36854     
36855 });
36856 /*
36857  * Based on:
36858  * Ext JS Library 1.1.1
36859  * Copyright(c) 2006-2007, Ext JS, LLC.
36860  *
36861  * Originally Released Under LGPL - original licence link has changed is not relivant.
36862  *
36863  * Fork - LGPL
36864  * <script type="text/javascript">
36865  */
36866
36867
36868 /**
36869  * @class Roo.bootstrap.SplitBar
36870  * @extends Roo.util.Observable
36871  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36872  * <br><br>
36873  * Usage:
36874  * <pre><code>
36875 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36876                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36877 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36878 split.minSize = 100;
36879 split.maxSize = 600;
36880 split.animate = true;
36881 split.on('moved', splitterMoved);
36882 </code></pre>
36883  * @constructor
36884  * Create a new SplitBar
36885  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36886  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36887  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36888  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36889                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36890                         position of the SplitBar).
36891  */
36892 Roo.bootstrap.SplitBar = function(cfg){
36893     
36894     /** @private */
36895     
36896     //{
36897     //  dragElement : elm
36898     //  resizingElement: el,
36899         // optional..
36900     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36901     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36902         // existingProxy ???
36903     //}
36904     
36905     this.el = Roo.get(cfg.dragElement, true);
36906     this.el.dom.unselectable = "on";
36907     /** @private */
36908     this.resizingEl = Roo.get(cfg.resizingElement, true);
36909
36910     /**
36911      * @private
36912      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36913      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36914      * @type Number
36915      */
36916     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36917     
36918     /**
36919      * The minimum size of the resizing element. (Defaults to 0)
36920      * @type Number
36921      */
36922     this.minSize = 0;
36923     
36924     /**
36925      * The maximum size of the resizing element. (Defaults to 2000)
36926      * @type Number
36927      */
36928     this.maxSize = 2000;
36929     
36930     /**
36931      * Whether to animate the transition to the new size
36932      * @type Boolean
36933      */
36934     this.animate = false;
36935     
36936     /**
36937      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36938      * @type Boolean
36939      */
36940     this.useShim = false;
36941     
36942     /** @private */
36943     this.shim = null;
36944     
36945     if(!cfg.existingProxy){
36946         /** @private */
36947         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36948     }else{
36949         this.proxy = Roo.get(cfg.existingProxy).dom;
36950     }
36951     /** @private */
36952     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36953     
36954     /** @private */
36955     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36956     
36957     /** @private */
36958     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36959     
36960     /** @private */
36961     this.dragSpecs = {};
36962     
36963     /**
36964      * @private The adapter to use to positon and resize elements
36965      */
36966     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36967     this.adapter.init(this);
36968     
36969     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36970         /** @private */
36971         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36972         this.el.addClass("roo-splitbar-h");
36973     }else{
36974         /** @private */
36975         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36976         this.el.addClass("roo-splitbar-v");
36977     }
36978     
36979     this.addEvents({
36980         /**
36981          * @event resize
36982          * Fires when the splitter is moved (alias for {@link #event-moved})
36983          * @param {Roo.bootstrap.SplitBar} this
36984          * @param {Number} newSize the new width or height
36985          */
36986         "resize" : true,
36987         /**
36988          * @event moved
36989          * Fires when the splitter is moved
36990          * @param {Roo.bootstrap.SplitBar} this
36991          * @param {Number} newSize the new width or height
36992          */
36993         "moved" : true,
36994         /**
36995          * @event beforeresize
36996          * Fires before the splitter is dragged
36997          * @param {Roo.bootstrap.SplitBar} this
36998          */
36999         "beforeresize" : true,
37000
37001         "beforeapply" : true
37002     });
37003
37004     Roo.util.Observable.call(this);
37005 };
37006
37007 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37008     onStartProxyDrag : function(x, y){
37009         this.fireEvent("beforeresize", this);
37010         if(!this.overlay){
37011             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37012             o.unselectable();
37013             o.enableDisplayMode("block");
37014             // all splitbars share the same overlay
37015             Roo.bootstrap.SplitBar.prototype.overlay = o;
37016         }
37017         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37018         this.overlay.show();
37019         Roo.get(this.proxy).setDisplayed("block");
37020         var size = this.adapter.getElementSize(this);
37021         this.activeMinSize = this.getMinimumSize();;
37022         this.activeMaxSize = this.getMaximumSize();;
37023         var c1 = size - this.activeMinSize;
37024         var c2 = Math.max(this.activeMaxSize - size, 0);
37025         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37026             this.dd.resetConstraints();
37027             this.dd.setXConstraint(
37028                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37029                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37030             );
37031             this.dd.setYConstraint(0, 0);
37032         }else{
37033             this.dd.resetConstraints();
37034             this.dd.setXConstraint(0, 0);
37035             this.dd.setYConstraint(
37036                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37037                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37038             );
37039          }
37040         this.dragSpecs.startSize = size;
37041         this.dragSpecs.startPoint = [x, y];
37042         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37043     },
37044     
37045     /** 
37046      * @private Called after the drag operation by the DDProxy
37047      */
37048     onEndProxyDrag : function(e){
37049         Roo.get(this.proxy).setDisplayed(false);
37050         var endPoint = Roo.lib.Event.getXY(e);
37051         if(this.overlay){
37052             this.overlay.hide();
37053         }
37054         var newSize;
37055         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37056             newSize = this.dragSpecs.startSize + 
37057                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37058                     endPoint[0] - this.dragSpecs.startPoint[0] :
37059                     this.dragSpecs.startPoint[0] - endPoint[0]
37060                 );
37061         }else{
37062             newSize = this.dragSpecs.startSize + 
37063                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37064                     endPoint[1] - this.dragSpecs.startPoint[1] :
37065                     this.dragSpecs.startPoint[1] - endPoint[1]
37066                 );
37067         }
37068         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37069         if(newSize != this.dragSpecs.startSize){
37070             if(this.fireEvent('beforeapply', this, newSize) !== false){
37071                 this.adapter.setElementSize(this, newSize);
37072                 this.fireEvent("moved", this, newSize);
37073                 this.fireEvent("resize", this, newSize);
37074             }
37075         }
37076     },
37077     
37078     /**
37079      * Get the adapter this SplitBar uses
37080      * @return The adapter object
37081      */
37082     getAdapter : function(){
37083         return this.adapter;
37084     },
37085     
37086     /**
37087      * Set the adapter this SplitBar uses
37088      * @param {Object} adapter A SplitBar adapter object
37089      */
37090     setAdapter : function(adapter){
37091         this.adapter = adapter;
37092         this.adapter.init(this);
37093     },
37094     
37095     /**
37096      * Gets the minimum size for the resizing element
37097      * @return {Number} The minimum size
37098      */
37099     getMinimumSize : function(){
37100         return this.minSize;
37101     },
37102     
37103     /**
37104      * Sets the minimum size for the resizing element
37105      * @param {Number} minSize The minimum size
37106      */
37107     setMinimumSize : function(minSize){
37108         this.minSize = minSize;
37109     },
37110     
37111     /**
37112      * Gets the maximum size for the resizing element
37113      * @return {Number} The maximum size
37114      */
37115     getMaximumSize : function(){
37116         return this.maxSize;
37117     },
37118     
37119     /**
37120      * Sets the maximum size for the resizing element
37121      * @param {Number} maxSize The maximum size
37122      */
37123     setMaximumSize : function(maxSize){
37124         this.maxSize = maxSize;
37125     },
37126     
37127     /**
37128      * Sets the initialize size for the resizing element
37129      * @param {Number} size The initial size
37130      */
37131     setCurrentSize : function(size){
37132         var oldAnimate = this.animate;
37133         this.animate = false;
37134         this.adapter.setElementSize(this, size);
37135         this.animate = oldAnimate;
37136     },
37137     
37138     /**
37139      * Destroy this splitbar. 
37140      * @param {Boolean} removeEl True to remove the element
37141      */
37142     destroy : function(removeEl){
37143         if(this.shim){
37144             this.shim.remove();
37145         }
37146         this.dd.unreg();
37147         this.proxy.parentNode.removeChild(this.proxy);
37148         if(removeEl){
37149             this.el.remove();
37150         }
37151     }
37152 });
37153
37154 /**
37155  * @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.
37156  */
37157 Roo.bootstrap.SplitBar.createProxy = function(dir){
37158     var proxy = new Roo.Element(document.createElement("div"));
37159     proxy.unselectable();
37160     var cls = 'roo-splitbar-proxy';
37161     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37162     document.body.appendChild(proxy.dom);
37163     return proxy.dom;
37164 };
37165
37166 /** 
37167  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37168  * Default Adapter. It assumes the splitter and resizing element are not positioned
37169  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37170  */
37171 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37172 };
37173
37174 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37175     // do nothing for now
37176     init : function(s){
37177     
37178     },
37179     /**
37180      * Called before drag operations to get the current size of the resizing element. 
37181      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37182      */
37183      getElementSize : function(s){
37184         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37185             return s.resizingEl.getWidth();
37186         }else{
37187             return s.resizingEl.getHeight();
37188         }
37189     },
37190     
37191     /**
37192      * Called after drag operations to set the size of the resizing element.
37193      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37194      * @param {Number} newSize The new size to set
37195      * @param {Function} onComplete A function to be invoked when resizing is complete
37196      */
37197     setElementSize : function(s, newSize, onComplete){
37198         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37199             if(!s.animate){
37200                 s.resizingEl.setWidth(newSize);
37201                 if(onComplete){
37202                     onComplete(s, newSize);
37203                 }
37204             }else{
37205                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37206             }
37207         }else{
37208             
37209             if(!s.animate){
37210                 s.resizingEl.setHeight(newSize);
37211                 if(onComplete){
37212                     onComplete(s, newSize);
37213                 }
37214             }else{
37215                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37216             }
37217         }
37218     }
37219 };
37220
37221 /** 
37222  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37223  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37224  * Adapter that  moves the splitter element to align with the resized sizing element. 
37225  * Used with an absolute positioned SplitBar.
37226  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37227  * document.body, make sure you assign an id to the body element.
37228  */
37229 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37230     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37231     this.container = Roo.get(container);
37232 };
37233
37234 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37235     init : function(s){
37236         this.basic.init(s);
37237     },
37238     
37239     getElementSize : function(s){
37240         return this.basic.getElementSize(s);
37241     },
37242     
37243     setElementSize : function(s, newSize, onComplete){
37244         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37245     },
37246     
37247     moveSplitter : function(s){
37248         var yes = Roo.bootstrap.SplitBar;
37249         switch(s.placement){
37250             case yes.LEFT:
37251                 s.el.setX(s.resizingEl.getRight());
37252                 break;
37253             case yes.RIGHT:
37254                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37255                 break;
37256             case yes.TOP:
37257                 s.el.setY(s.resizingEl.getBottom());
37258                 break;
37259             case yes.BOTTOM:
37260                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37261                 break;
37262         }
37263     }
37264 };
37265
37266 /**
37267  * Orientation constant - Create a vertical SplitBar
37268  * @static
37269  * @type Number
37270  */
37271 Roo.bootstrap.SplitBar.VERTICAL = 1;
37272
37273 /**
37274  * Orientation constant - Create a horizontal SplitBar
37275  * @static
37276  * @type Number
37277  */
37278 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37279
37280 /**
37281  * Placement constant - The resizing element is to the left of the splitter element
37282  * @static
37283  * @type Number
37284  */
37285 Roo.bootstrap.SplitBar.LEFT = 1;
37286
37287 /**
37288  * Placement constant - The resizing element is to the right of the splitter element
37289  * @static
37290  * @type Number
37291  */
37292 Roo.bootstrap.SplitBar.RIGHT = 2;
37293
37294 /**
37295  * Placement constant - The resizing element is positioned above the splitter element
37296  * @static
37297  * @type Number
37298  */
37299 Roo.bootstrap.SplitBar.TOP = 3;
37300
37301 /**
37302  * Placement constant - The resizing element is positioned under splitter element
37303  * @static
37304  * @type Number
37305  */
37306 Roo.bootstrap.SplitBar.BOTTOM = 4;
37307 Roo.namespace("Roo.bootstrap.layout");/*
37308  * Based on:
37309  * Ext JS Library 1.1.1
37310  * Copyright(c) 2006-2007, Ext JS, LLC.
37311  *
37312  * Originally Released Under LGPL - original licence link has changed is not relivant.
37313  *
37314  * Fork - LGPL
37315  * <script type="text/javascript">
37316  */
37317
37318 /**
37319  * @class Roo.bootstrap.layout.Manager
37320  * @extends Roo.bootstrap.Component
37321  * Base class for layout managers.
37322  */
37323 Roo.bootstrap.layout.Manager = function(config)
37324 {
37325     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37326
37327
37328
37329
37330
37331     /** false to disable window resize monitoring @type Boolean */
37332     this.monitorWindowResize = true;
37333     this.regions = {};
37334     this.addEvents({
37335         /**
37336          * @event layout
37337          * Fires when a layout is performed.
37338          * @param {Roo.LayoutManager} this
37339          */
37340         "layout" : true,
37341         /**
37342          * @event regionresized
37343          * Fires when the user resizes a region.
37344          * @param {Roo.LayoutRegion} region The resized region
37345          * @param {Number} newSize The new size (width for east/west, height for north/south)
37346          */
37347         "regionresized" : true,
37348         /**
37349          * @event regioncollapsed
37350          * Fires when a region is collapsed.
37351          * @param {Roo.LayoutRegion} region The collapsed region
37352          */
37353         "regioncollapsed" : true,
37354         /**
37355          * @event regionexpanded
37356          * Fires when a region is expanded.
37357          * @param {Roo.LayoutRegion} region The expanded region
37358          */
37359         "regionexpanded" : true
37360     });
37361     this.updating = false;
37362
37363     if (config.el) {
37364         this.el = Roo.get(config.el);
37365         this.initEvents();
37366     }
37367
37368 };
37369
37370 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37371
37372
37373     regions : null,
37374
37375     monitorWindowResize : true,
37376
37377
37378     updating : false,
37379
37380
37381     onRender : function(ct, position)
37382     {
37383         if(!this.el){
37384             this.el = Roo.get(ct);
37385             this.initEvents();
37386         }
37387         //this.fireEvent('render',this);
37388     },
37389
37390
37391     initEvents: function()
37392     {
37393
37394
37395         // ie scrollbar fix
37396         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37397             document.body.scroll = "no";
37398         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37399             this.el.position('relative');
37400         }
37401         this.id = this.el.id;
37402         this.el.addClass("roo-layout-container");
37403         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37404         if(this.el.dom != document.body ) {
37405             this.el.on('resize', this.layout,this);
37406             this.el.on('show', this.layout,this);
37407         }
37408
37409     },
37410
37411     /**
37412      * Returns true if this layout is currently being updated
37413      * @return {Boolean}
37414      */
37415     isUpdating : function(){
37416         return this.updating;
37417     },
37418
37419     /**
37420      * Suspend the LayoutManager from doing auto-layouts while
37421      * making multiple add or remove calls
37422      */
37423     beginUpdate : function(){
37424         this.updating = true;
37425     },
37426
37427     /**
37428      * Restore auto-layouts and optionally disable the manager from performing a layout
37429      * @param {Boolean} noLayout true to disable a layout update
37430      */
37431     endUpdate : function(noLayout){
37432         this.updating = false;
37433         if(!noLayout){
37434             this.layout();
37435         }
37436     },
37437
37438     layout: function(){
37439         // abstract...
37440     },
37441
37442     onRegionResized : function(region, newSize){
37443         this.fireEvent("regionresized", region, newSize);
37444         this.layout();
37445     },
37446
37447     onRegionCollapsed : function(region){
37448         this.fireEvent("regioncollapsed", region);
37449     },
37450
37451     onRegionExpanded : function(region){
37452         this.fireEvent("regionexpanded", region);
37453     },
37454
37455     /**
37456      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37457      * performs box-model adjustments.
37458      * @return {Object} The size as an object {width: (the width), height: (the height)}
37459      */
37460     getViewSize : function()
37461     {
37462         var size;
37463         if(this.el.dom != document.body){
37464             size = this.el.getSize();
37465         }else{
37466             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37467         }
37468         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37469         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37470         return size;
37471     },
37472
37473     /**
37474      * Returns the Element this layout is bound to.
37475      * @return {Roo.Element}
37476      */
37477     getEl : function(){
37478         return this.el;
37479     },
37480
37481     /**
37482      * Returns the specified region.
37483      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37484      * @return {Roo.LayoutRegion}
37485      */
37486     getRegion : function(target){
37487         return this.regions[target.toLowerCase()];
37488     },
37489
37490     onWindowResize : function(){
37491         if(this.monitorWindowResize){
37492             this.layout();
37493         }
37494     }
37495 });
37496 /*
37497  * Based on:
37498  * Ext JS Library 1.1.1
37499  * Copyright(c) 2006-2007, Ext JS, LLC.
37500  *
37501  * Originally Released Under LGPL - original licence link has changed is not relivant.
37502  *
37503  * Fork - LGPL
37504  * <script type="text/javascript">
37505  */
37506 /**
37507  * @class Roo.bootstrap.layout.Border
37508  * @extends Roo.bootstrap.layout.Manager
37509  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37510  * please see: examples/bootstrap/nested.html<br><br>
37511  
37512 <b>The container the layout is rendered into can be either the body element or any other element.
37513 If it is not the body element, the container needs to either be an absolute positioned element,
37514 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37515 the container size if it is not the body element.</b>
37516
37517 * @constructor
37518 * Create a new Border
37519 * @param {Object} config Configuration options
37520  */
37521 Roo.bootstrap.layout.Border = function(config){
37522     config = config || {};
37523     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37524     
37525     
37526     
37527     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37528         if(config[region]){
37529             config[region].region = region;
37530             this.addRegion(config[region]);
37531         }
37532     },this);
37533     
37534 };
37535
37536 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37537
37538 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37539     
37540     parent : false, // this might point to a 'nest' or a ???
37541     
37542     /**
37543      * Creates and adds a new region if it doesn't already exist.
37544      * @param {String} target The target region key (north, south, east, west or center).
37545      * @param {Object} config The regions config object
37546      * @return {BorderLayoutRegion} The new region
37547      */
37548     addRegion : function(config)
37549     {
37550         if(!this.regions[config.region]){
37551             var r = this.factory(config);
37552             this.bindRegion(r);
37553         }
37554         return this.regions[config.region];
37555     },
37556
37557     // private (kinda)
37558     bindRegion : function(r){
37559         this.regions[r.config.region] = r;
37560         
37561         r.on("visibilitychange",    this.layout, this);
37562         r.on("paneladded",          this.layout, this);
37563         r.on("panelremoved",        this.layout, this);
37564         r.on("invalidated",         this.layout, this);
37565         r.on("resized",             this.onRegionResized, this);
37566         r.on("collapsed",           this.onRegionCollapsed, this);
37567         r.on("expanded",            this.onRegionExpanded, this);
37568     },
37569
37570     /**
37571      * Performs a layout update.
37572      */
37573     layout : function()
37574     {
37575         if(this.updating) {
37576             return;
37577         }
37578         
37579         // render all the rebions if they have not been done alreayd?
37580         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37581             if(this.regions[region] && !this.regions[region].bodyEl){
37582                 this.regions[region].onRender(this.el)
37583             }
37584         },this);
37585         
37586         var size = this.getViewSize();
37587         var w = size.width;
37588         var h = size.height;
37589         var centerW = w;
37590         var centerH = h;
37591         var centerY = 0;
37592         var centerX = 0;
37593         //var x = 0, y = 0;
37594
37595         var rs = this.regions;
37596         var north = rs["north"];
37597         var south = rs["south"]; 
37598         var west = rs["west"];
37599         var east = rs["east"];
37600         var center = rs["center"];
37601         //if(this.hideOnLayout){ // not supported anymore
37602             //c.el.setStyle("display", "none");
37603         //}
37604         if(north && north.isVisible()){
37605             var b = north.getBox();
37606             var m = north.getMargins();
37607             b.width = w - (m.left+m.right);
37608             b.x = m.left;
37609             b.y = m.top;
37610             centerY = b.height + b.y + m.bottom;
37611             centerH -= centerY;
37612             north.updateBox(this.safeBox(b));
37613         }
37614         if(south && south.isVisible()){
37615             var b = south.getBox();
37616             var m = south.getMargins();
37617             b.width = w - (m.left+m.right);
37618             b.x = m.left;
37619             var totalHeight = (b.height + m.top + m.bottom);
37620             b.y = h - totalHeight + m.top;
37621             centerH -= totalHeight;
37622             south.updateBox(this.safeBox(b));
37623         }
37624         if(west && west.isVisible()){
37625             var b = west.getBox();
37626             var m = west.getMargins();
37627             b.height = centerH - (m.top+m.bottom);
37628             b.x = m.left;
37629             b.y = centerY + m.top;
37630             var totalWidth = (b.width + m.left + m.right);
37631             centerX += totalWidth;
37632             centerW -= totalWidth;
37633             west.updateBox(this.safeBox(b));
37634         }
37635         if(east && east.isVisible()){
37636             var b = east.getBox();
37637             var m = east.getMargins();
37638             b.height = centerH - (m.top+m.bottom);
37639             var totalWidth = (b.width + m.left + m.right);
37640             b.x = w - totalWidth + m.left;
37641             b.y = centerY + m.top;
37642             centerW -= totalWidth;
37643             east.updateBox(this.safeBox(b));
37644         }
37645         if(center){
37646             var m = center.getMargins();
37647             var centerBox = {
37648                 x: centerX + m.left,
37649                 y: centerY + m.top,
37650                 width: centerW - (m.left+m.right),
37651                 height: centerH - (m.top+m.bottom)
37652             };
37653             //if(this.hideOnLayout){
37654                 //center.el.setStyle("display", "block");
37655             //}
37656             center.updateBox(this.safeBox(centerBox));
37657         }
37658         this.el.repaint();
37659         this.fireEvent("layout", this);
37660     },
37661
37662     // private
37663     safeBox : function(box){
37664         box.width = Math.max(0, box.width);
37665         box.height = Math.max(0, box.height);
37666         return box;
37667     },
37668
37669     /**
37670      * Adds a ContentPanel (or subclass) to this layout.
37671      * @param {String} target The target region key (north, south, east, west or center).
37672      * @param {Roo.ContentPanel} panel The panel to add
37673      * @return {Roo.ContentPanel} The added panel
37674      */
37675     add : function(target, panel){
37676          
37677         target = target.toLowerCase();
37678         return this.regions[target].add(panel);
37679     },
37680
37681     /**
37682      * Remove a ContentPanel (or subclass) to this layout.
37683      * @param {String} target The target region key (north, south, east, west or center).
37684      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37685      * @return {Roo.ContentPanel} The removed panel
37686      */
37687     remove : function(target, panel){
37688         target = target.toLowerCase();
37689         return this.regions[target].remove(panel);
37690     },
37691
37692     /**
37693      * Searches all regions for a panel with the specified id
37694      * @param {String} panelId
37695      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37696      */
37697     findPanel : function(panelId){
37698         var rs = this.regions;
37699         for(var target in rs){
37700             if(typeof rs[target] != "function"){
37701                 var p = rs[target].getPanel(panelId);
37702                 if(p){
37703                     return p;
37704                 }
37705             }
37706         }
37707         return null;
37708     },
37709
37710     /**
37711      * Searches all regions for a panel with the specified id and activates (shows) it.
37712      * @param {String/ContentPanel} panelId The panels id or the panel itself
37713      * @return {Roo.ContentPanel} The shown panel or null
37714      */
37715     showPanel : function(panelId) {
37716       var rs = this.regions;
37717       for(var target in rs){
37718          var r = rs[target];
37719          if(typeof r != "function"){
37720             if(r.hasPanel(panelId)){
37721                return r.showPanel(panelId);
37722             }
37723          }
37724       }
37725       return null;
37726    },
37727
37728    /**
37729      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37730      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37731      */
37732    /*
37733     restoreState : function(provider){
37734         if(!provider){
37735             provider = Roo.state.Manager;
37736         }
37737         var sm = new Roo.LayoutStateManager();
37738         sm.init(this, provider);
37739     },
37740 */
37741  
37742  
37743     /**
37744      * Adds a xtype elements to the layout.
37745      * <pre><code>
37746
37747 layout.addxtype({
37748        xtype : 'ContentPanel',
37749        region: 'west',
37750        items: [ .... ]
37751    }
37752 );
37753
37754 layout.addxtype({
37755         xtype : 'NestedLayoutPanel',
37756         region: 'west',
37757         layout: {
37758            center: { },
37759            west: { }   
37760         },
37761         items : [ ... list of content panels or nested layout panels.. ]
37762    }
37763 );
37764 </code></pre>
37765      * @param {Object} cfg Xtype definition of item to add.
37766      */
37767     addxtype : function(cfg)
37768     {
37769         // basically accepts a pannel...
37770         // can accept a layout region..!?!?
37771         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37772         
37773         
37774         // theory?  children can only be panels??
37775         
37776         //if (!cfg.xtype.match(/Panel$/)) {
37777         //    return false;
37778         //}
37779         var ret = false;
37780         
37781         if (typeof(cfg.region) == 'undefined') {
37782             Roo.log("Failed to add Panel, region was not set");
37783             Roo.log(cfg);
37784             return false;
37785         }
37786         var region = cfg.region;
37787         delete cfg.region;
37788         
37789           
37790         var xitems = [];
37791         if (cfg.items) {
37792             xitems = cfg.items;
37793             delete cfg.items;
37794         }
37795         var nb = false;
37796         
37797         if ( region == 'center') {
37798             Roo.log("Center: " + cfg.title);
37799         }
37800         
37801         
37802         switch(cfg.xtype) 
37803         {
37804             case 'Content':  // ContentPanel (el, cfg)
37805             case 'Scroll':  // ContentPanel (el, cfg)
37806             case 'View': 
37807                 cfg.autoCreate = cfg.autoCreate || true;
37808                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37809                 //} else {
37810                 //    var el = this.el.createChild();
37811                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37812                 //}
37813                 
37814                 this.add(region, ret);
37815                 break;
37816             
37817             /*
37818             case 'TreePanel': // our new panel!
37819                 cfg.el = this.el.createChild();
37820                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37821                 this.add(region, ret);
37822                 break;
37823             */
37824             
37825             case 'Nest': 
37826                 // create a new Layout (which is  a Border Layout...
37827                 
37828                 var clayout = cfg.layout;
37829                 clayout.el  = this.el.createChild();
37830                 clayout.items   = clayout.items  || [];
37831                 
37832                 delete cfg.layout;
37833                 
37834                 // replace this exitems with the clayout ones..
37835                 xitems = clayout.items;
37836                  
37837                 // force background off if it's in center...
37838                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37839                     cfg.background = false;
37840                 }
37841                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37842                 
37843                 
37844                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37845                 //console.log('adding nested layout panel '  + cfg.toSource());
37846                 this.add(region, ret);
37847                 nb = {}; /// find first...
37848                 break;
37849             
37850             case 'Grid':
37851                 
37852                 // needs grid and region
37853                 
37854                 //var el = this.getRegion(region).el.createChild();
37855                 /*
37856                  *var el = this.el.createChild();
37857                 // create the grid first...
37858                 cfg.grid.container = el;
37859                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37860                 */
37861                 
37862                 if (region == 'center' && this.active ) {
37863                     cfg.background = false;
37864                 }
37865                 
37866                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37867                 
37868                 this.add(region, ret);
37869                 /*
37870                 if (cfg.background) {
37871                     // render grid on panel activation (if panel background)
37872                     ret.on('activate', function(gp) {
37873                         if (!gp.grid.rendered) {
37874                     //        gp.grid.render(el);
37875                         }
37876                     });
37877                 } else {
37878                   //  cfg.grid.render(el);
37879                 }
37880                 */
37881                 break;
37882            
37883            
37884             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37885                 // it was the old xcomponent building that caused this before.
37886                 // espeically if border is the top element in the tree.
37887                 ret = this;
37888                 break; 
37889                 
37890                     
37891                 
37892                 
37893                 
37894             default:
37895                 /*
37896                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37897                     
37898                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37899                     this.add(region, ret);
37900                 } else {
37901                 */
37902                     Roo.log(cfg);
37903                     throw "Can not add '" + cfg.xtype + "' to Border";
37904                     return null;
37905              
37906                                 
37907              
37908         }
37909         this.beginUpdate();
37910         // add children..
37911         var region = '';
37912         var abn = {};
37913         Roo.each(xitems, function(i)  {
37914             region = nb && i.region ? i.region : false;
37915             
37916             var add = ret.addxtype(i);
37917            
37918             if (region) {
37919                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37920                 if (!i.background) {
37921                     abn[region] = nb[region] ;
37922                 }
37923             }
37924             
37925         });
37926         this.endUpdate();
37927
37928         // make the last non-background panel active..
37929         //if (nb) { Roo.log(abn); }
37930         if (nb) {
37931             
37932             for(var r in abn) {
37933                 region = this.getRegion(r);
37934                 if (region) {
37935                     // tried using nb[r], but it does not work..
37936                      
37937                     region.showPanel(abn[r]);
37938                    
37939                 }
37940             }
37941         }
37942         return ret;
37943         
37944     },
37945     
37946     
37947 // private
37948     factory : function(cfg)
37949     {
37950         
37951         var validRegions = Roo.bootstrap.layout.Border.regions;
37952
37953         var target = cfg.region;
37954         cfg.mgr = this;
37955         
37956         var r = Roo.bootstrap.layout;
37957         Roo.log(target);
37958         switch(target){
37959             case "north":
37960                 return new r.North(cfg);
37961             case "south":
37962                 return new r.South(cfg);
37963             case "east":
37964                 return new r.East(cfg);
37965             case "west":
37966                 return new r.West(cfg);
37967             case "center":
37968                 return new r.Center(cfg);
37969         }
37970         throw 'Layout region "'+target+'" not supported.';
37971     }
37972     
37973     
37974 });
37975  /*
37976  * Based on:
37977  * Ext JS Library 1.1.1
37978  * Copyright(c) 2006-2007, Ext JS, LLC.
37979  *
37980  * Originally Released Under LGPL - original licence link has changed is not relivant.
37981  *
37982  * Fork - LGPL
37983  * <script type="text/javascript">
37984  */
37985  
37986 /**
37987  * @class Roo.bootstrap.layout.Basic
37988  * @extends Roo.util.Observable
37989  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37990  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37991  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37992  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37993  * @cfg {string}   region  the region that it inhabits..
37994  * @cfg {bool}   skipConfig skip config?
37995  * 
37996
37997  */
37998 Roo.bootstrap.layout.Basic = function(config){
37999     
38000     this.mgr = config.mgr;
38001     
38002     this.position = config.region;
38003     
38004     var skipConfig = config.skipConfig;
38005     
38006     this.events = {
38007         /**
38008          * @scope Roo.BasicLayoutRegion
38009          */
38010         
38011         /**
38012          * @event beforeremove
38013          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38014          * @param {Roo.LayoutRegion} this
38015          * @param {Roo.ContentPanel} panel The panel
38016          * @param {Object} e The cancel event object
38017          */
38018         "beforeremove" : true,
38019         /**
38020          * @event invalidated
38021          * Fires when the layout for this region is changed.
38022          * @param {Roo.LayoutRegion} this
38023          */
38024         "invalidated" : true,
38025         /**
38026          * @event visibilitychange
38027          * Fires when this region is shown or hidden 
38028          * @param {Roo.LayoutRegion} this
38029          * @param {Boolean} visibility true or false
38030          */
38031         "visibilitychange" : true,
38032         /**
38033          * @event paneladded
38034          * Fires when a panel is added. 
38035          * @param {Roo.LayoutRegion} this
38036          * @param {Roo.ContentPanel} panel The panel
38037          */
38038         "paneladded" : true,
38039         /**
38040          * @event panelremoved
38041          * Fires when a panel is removed. 
38042          * @param {Roo.LayoutRegion} this
38043          * @param {Roo.ContentPanel} panel The panel
38044          */
38045         "panelremoved" : true,
38046         /**
38047          * @event beforecollapse
38048          * Fires when this region before collapse.
38049          * @param {Roo.LayoutRegion} this
38050          */
38051         "beforecollapse" : true,
38052         /**
38053          * @event collapsed
38054          * Fires when this region is collapsed.
38055          * @param {Roo.LayoutRegion} this
38056          */
38057         "collapsed" : true,
38058         /**
38059          * @event expanded
38060          * Fires when this region is expanded.
38061          * @param {Roo.LayoutRegion} this
38062          */
38063         "expanded" : true,
38064         /**
38065          * @event slideshow
38066          * Fires when this region is slid into view.
38067          * @param {Roo.LayoutRegion} this
38068          */
38069         "slideshow" : true,
38070         /**
38071          * @event slidehide
38072          * Fires when this region slides out of view. 
38073          * @param {Roo.LayoutRegion} this
38074          */
38075         "slidehide" : true,
38076         /**
38077          * @event panelactivated
38078          * Fires when a panel is activated. 
38079          * @param {Roo.LayoutRegion} this
38080          * @param {Roo.ContentPanel} panel The activated panel
38081          */
38082         "panelactivated" : true,
38083         /**
38084          * @event resized
38085          * Fires when the user resizes this region. 
38086          * @param {Roo.LayoutRegion} this
38087          * @param {Number} newSize The new size (width for east/west, height for north/south)
38088          */
38089         "resized" : true
38090     };
38091     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38092     this.panels = new Roo.util.MixedCollection();
38093     this.panels.getKey = this.getPanelId.createDelegate(this);
38094     this.box = null;
38095     this.activePanel = null;
38096     // ensure listeners are added...
38097     
38098     if (config.listeners || config.events) {
38099         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38100             listeners : config.listeners || {},
38101             events : config.events || {}
38102         });
38103     }
38104     
38105     if(skipConfig !== true){
38106         this.applyConfig(config);
38107     }
38108 };
38109
38110 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38111 {
38112     getPanelId : function(p){
38113         return p.getId();
38114     },
38115     
38116     applyConfig : function(config){
38117         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38118         this.config = config;
38119         
38120     },
38121     
38122     /**
38123      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38124      * the width, for horizontal (north, south) the height.
38125      * @param {Number} newSize The new width or height
38126      */
38127     resizeTo : function(newSize){
38128         var el = this.el ? this.el :
38129                  (this.activePanel ? this.activePanel.getEl() : null);
38130         if(el){
38131             switch(this.position){
38132                 case "east":
38133                 case "west":
38134                     el.setWidth(newSize);
38135                     this.fireEvent("resized", this, newSize);
38136                 break;
38137                 case "north":
38138                 case "south":
38139                     el.setHeight(newSize);
38140                     this.fireEvent("resized", this, newSize);
38141                 break;                
38142             }
38143         }
38144     },
38145     
38146     getBox : function(){
38147         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38148     },
38149     
38150     getMargins : function(){
38151         return this.margins;
38152     },
38153     
38154     updateBox : function(box){
38155         this.box = box;
38156         var el = this.activePanel.getEl();
38157         el.dom.style.left = box.x + "px";
38158         el.dom.style.top = box.y + "px";
38159         this.activePanel.setSize(box.width, box.height);
38160     },
38161     
38162     /**
38163      * Returns the container element for this region.
38164      * @return {Roo.Element}
38165      */
38166     getEl : function(){
38167         return this.activePanel;
38168     },
38169     
38170     /**
38171      * Returns true if this region is currently visible.
38172      * @return {Boolean}
38173      */
38174     isVisible : function(){
38175         return this.activePanel ? true : false;
38176     },
38177     
38178     setActivePanel : function(panel){
38179         panel = this.getPanel(panel);
38180         if(this.activePanel && this.activePanel != panel){
38181             this.activePanel.setActiveState(false);
38182             this.activePanel.getEl().setLeftTop(-10000,-10000);
38183         }
38184         this.activePanel = panel;
38185         panel.setActiveState(true);
38186         if(this.box){
38187             panel.setSize(this.box.width, this.box.height);
38188         }
38189         this.fireEvent("panelactivated", this, panel);
38190         this.fireEvent("invalidated");
38191     },
38192     
38193     /**
38194      * Show the specified panel.
38195      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38196      * @return {Roo.ContentPanel} The shown panel or null
38197      */
38198     showPanel : function(panel){
38199         panel = this.getPanel(panel);
38200         if(panel){
38201             this.setActivePanel(panel);
38202         }
38203         return panel;
38204     },
38205     
38206     /**
38207      * Get the active panel for this region.
38208      * @return {Roo.ContentPanel} The active panel or null
38209      */
38210     getActivePanel : function(){
38211         return this.activePanel;
38212     },
38213     
38214     /**
38215      * Add the passed ContentPanel(s)
38216      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38217      * @return {Roo.ContentPanel} The panel added (if only one was added)
38218      */
38219     add : function(panel){
38220         if(arguments.length > 1){
38221             for(var i = 0, len = arguments.length; i < len; i++) {
38222                 this.add(arguments[i]);
38223             }
38224             return null;
38225         }
38226         if(this.hasPanel(panel)){
38227             this.showPanel(panel);
38228             return panel;
38229         }
38230         var el = panel.getEl();
38231         if(el.dom.parentNode != this.mgr.el.dom){
38232             this.mgr.el.dom.appendChild(el.dom);
38233         }
38234         if(panel.setRegion){
38235             panel.setRegion(this);
38236         }
38237         this.panels.add(panel);
38238         el.setStyle("position", "absolute");
38239         if(!panel.background){
38240             this.setActivePanel(panel);
38241             if(this.config.initialSize && this.panels.getCount()==1){
38242                 this.resizeTo(this.config.initialSize);
38243             }
38244         }
38245         this.fireEvent("paneladded", this, panel);
38246         return panel;
38247     },
38248     
38249     /**
38250      * Returns true if the panel is in this region.
38251      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38252      * @return {Boolean}
38253      */
38254     hasPanel : function(panel){
38255         if(typeof panel == "object"){ // must be panel obj
38256             panel = panel.getId();
38257         }
38258         return this.getPanel(panel) ? true : false;
38259     },
38260     
38261     /**
38262      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38263      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38264      * @param {Boolean} preservePanel Overrides the config preservePanel option
38265      * @return {Roo.ContentPanel} The panel that was removed
38266      */
38267     remove : function(panel, preservePanel){
38268         panel = this.getPanel(panel);
38269         if(!panel){
38270             return null;
38271         }
38272         var e = {};
38273         this.fireEvent("beforeremove", this, panel, e);
38274         if(e.cancel === true){
38275             return null;
38276         }
38277         var panelId = panel.getId();
38278         this.panels.removeKey(panelId);
38279         return panel;
38280     },
38281     
38282     /**
38283      * Returns the panel specified or null if it's not in this region.
38284      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38285      * @return {Roo.ContentPanel}
38286      */
38287     getPanel : function(id){
38288         if(typeof id == "object"){ // must be panel obj
38289             return id;
38290         }
38291         return this.panels.get(id);
38292     },
38293     
38294     /**
38295      * Returns this regions position (north/south/east/west/center).
38296      * @return {String} 
38297      */
38298     getPosition: function(){
38299         return this.position;    
38300     }
38301 });/*
38302  * Based on:
38303  * Ext JS Library 1.1.1
38304  * Copyright(c) 2006-2007, Ext JS, LLC.
38305  *
38306  * Originally Released Under LGPL - original licence link has changed is not relivant.
38307  *
38308  * Fork - LGPL
38309  * <script type="text/javascript">
38310  */
38311  
38312 /**
38313  * @class Roo.bootstrap.layout.Region
38314  * @extends Roo.bootstrap.layout.Basic
38315  * This class represents a region in a layout manager.
38316  
38317  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38318  * @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})
38319  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38320  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38321  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38322  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38323  * @cfg {String}    title           The title for the region (overrides panel titles)
38324  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38325  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38326  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38327  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38328  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38329  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38330  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38331  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38332  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38333  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38334
38335  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38336  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38337  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38338  * @cfg {Number}    width           For East/West panels
38339  * @cfg {Number}    height          For North/South panels
38340  * @cfg {Boolean}   split           To show the splitter
38341  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38342  * 
38343  * @cfg {string}   cls             Extra CSS classes to add to region
38344  * 
38345  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38346  * @cfg {string}   region  the region that it inhabits..
38347  *
38348
38349  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38350  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38351
38352  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38353  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38354  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38355  */
38356 Roo.bootstrap.layout.Region = function(config)
38357 {
38358     this.applyConfig(config);
38359
38360     var mgr = config.mgr;
38361     var pos = config.region;
38362     config.skipConfig = true;
38363     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38364     
38365     if (mgr.el) {
38366         this.onRender(mgr.el);   
38367     }
38368      
38369     this.visible = true;
38370     this.collapsed = false;
38371     this.unrendered_panels = [];
38372 };
38373
38374 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38375
38376     position: '', // set by wrapper (eg. north/south etc..)
38377     unrendered_panels : null,  // unrendered panels.
38378     
38379     tabPosition : false,
38380     
38381     mgr: false, // points to 'Border'
38382     
38383     
38384     createBody : function(){
38385         /** This region's body element 
38386         * @type Roo.Element */
38387         this.bodyEl = this.el.createChild({
38388                 tag: "div",
38389                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38390         });
38391     },
38392
38393     onRender: function(ctr, pos)
38394     {
38395         var dh = Roo.DomHelper;
38396         /** This region's container element 
38397         * @type Roo.Element */
38398         this.el = dh.append(ctr.dom, {
38399                 tag: "div",
38400                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38401             }, true);
38402         /** This region's title element 
38403         * @type Roo.Element */
38404     
38405         this.titleEl = dh.append(this.el.dom,  {
38406                 tag: "div",
38407                 unselectable: "on",
38408                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38409                 children:[
38410                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38411                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38412                 ]
38413             }, true);
38414         
38415         this.titleEl.enableDisplayMode();
38416         /** This region's title text element 
38417         * @type HTMLElement */
38418         this.titleTextEl = this.titleEl.dom.firstChild;
38419         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38420         /*
38421         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38422         this.closeBtn.enableDisplayMode();
38423         this.closeBtn.on("click", this.closeClicked, this);
38424         this.closeBtn.hide();
38425     */
38426         this.createBody(this.config);
38427         if(this.config.hideWhenEmpty){
38428             this.hide();
38429             this.on("paneladded", this.validateVisibility, this);
38430             this.on("panelremoved", this.validateVisibility, this);
38431         }
38432         if(this.autoScroll){
38433             this.bodyEl.setStyle("overflow", "auto");
38434         }else{
38435             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38436         }
38437         //if(c.titlebar !== false){
38438             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38439                 this.titleEl.hide();
38440             }else{
38441                 this.titleEl.show();
38442                 if(this.config.title){
38443                     this.titleTextEl.innerHTML = this.config.title;
38444                 }
38445             }
38446         //}
38447         if(this.config.collapsed){
38448             this.collapse(true);
38449         }
38450         if(this.config.hidden){
38451             this.hide();
38452         }
38453         
38454         if (this.unrendered_panels && this.unrendered_panels.length) {
38455             for (var i =0;i< this.unrendered_panels.length; i++) {
38456                 this.add(this.unrendered_panels[i]);
38457             }
38458             this.unrendered_panels = null;
38459             
38460         }
38461         
38462     },
38463     
38464     applyConfig : function(c)
38465     {
38466         /*
38467          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38468             var dh = Roo.DomHelper;
38469             if(c.titlebar !== false){
38470                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38471                 this.collapseBtn.on("click", this.collapse, this);
38472                 this.collapseBtn.enableDisplayMode();
38473                 /*
38474                 if(c.showPin === true || this.showPin){
38475                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38476                     this.stickBtn.enableDisplayMode();
38477                     this.stickBtn.on("click", this.expand, this);
38478                     this.stickBtn.hide();
38479                 }
38480                 
38481             }
38482             */
38483             /** This region's collapsed element
38484             * @type Roo.Element */
38485             /*
38486              *
38487             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38488                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38489             ]}, true);
38490             
38491             if(c.floatable !== false){
38492                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38493                this.collapsedEl.on("click", this.collapseClick, this);
38494             }
38495
38496             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38497                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38498                    id: "message", unselectable: "on", style:{"float":"left"}});
38499                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38500              }
38501             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38502             this.expandBtn.on("click", this.expand, this);
38503             
38504         }
38505         
38506         if(this.collapseBtn){
38507             this.collapseBtn.setVisible(c.collapsible == true);
38508         }
38509         
38510         this.cmargins = c.cmargins || this.cmargins ||
38511                          (this.position == "west" || this.position == "east" ?
38512                              {top: 0, left: 2, right:2, bottom: 0} :
38513                              {top: 2, left: 0, right:0, bottom: 2});
38514         */
38515         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38516         
38517         
38518         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38519         
38520         this.autoScroll = c.autoScroll || false;
38521         
38522         
38523        
38524         
38525         this.duration = c.duration || .30;
38526         this.slideDuration = c.slideDuration || .45;
38527         this.config = c;
38528        
38529     },
38530     /**
38531      * Returns true if this region is currently visible.
38532      * @return {Boolean}
38533      */
38534     isVisible : function(){
38535         return this.visible;
38536     },
38537
38538     /**
38539      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38540      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38541      */
38542     //setCollapsedTitle : function(title){
38543     //    title = title || "&#160;";
38544      //   if(this.collapsedTitleTextEl){
38545       //      this.collapsedTitleTextEl.innerHTML = title;
38546        // }
38547     //},
38548
38549     getBox : function(){
38550         var b;
38551       //  if(!this.collapsed){
38552             b = this.el.getBox(false, true);
38553        // }else{
38554           //  b = this.collapsedEl.getBox(false, true);
38555         //}
38556         return b;
38557     },
38558
38559     getMargins : function(){
38560         return this.margins;
38561         //return this.collapsed ? this.cmargins : this.margins;
38562     },
38563 /*
38564     highlight : function(){
38565         this.el.addClass("x-layout-panel-dragover");
38566     },
38567
38568     unhighlight : function(){
38569         this.el.removeClass("x-layout-panel-dragover");
38570     },
38571 */
38572     updateBox : function(box)
38573     {
38574         if (!this.bodyEl) {
38575             return; // not rendered yet..
38576         }
38577         
38578         this.box = box;
38579         if(!this.collapsed){
38580             this.el.dom.style.left = box.x + "px";
38581             this.el.dom.style.top = box.y + "px";
38582             this.updateBody(box.width, box.height);
38583         }else{
38584             this.collapsedEl.dom.style.left = box.x + "px";
38585             this.collapsedEl.dom.style.top = box.y + "px";
38586             this.collapsedEl.setSize(box.width, box.height);
38587         }
38588         if(this.tabs){
38589             this.tabs.autoSizeTabs();
38590         }
38591     },
38592
38593     updateBody : function(w, h)
38594     {
38595         if(w !== null){
38596             this.el.setWidth(w);
38597             w -= this.el.getBorderWidth("rl");
38598             if(this.config.adjustments){
38599                 w += this.config.adjustments[0];
38600             }
38601         }
38602         if(h !== null && h > 0){
38603             this.el.setHeight(h);
38604             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38605             h -= this.el.getBorderWidth("tb");
38606             if(this.config.adjustments){
38607                 h += this.config.adjustments[1];
38608             }
38609             this.bodyEl.setHeight(h);
38610             if(this.tabs){
38611                 h = this.tabs.syncHeight(h);
38612             }
38613         }
38614         if(this.panelSize){
38615             w = w !== null ? w : this.panelSize.width;
38616             h = h !== null ? h : this.panelSize.height;
38617         }
38618         if(this.activePanel){
38619             var el = this.activePanel.getEl();
38620             w = w !== null ? w : el.getWidth();
38621             h = h !== null ? h : el.getHeight();
38622             this.panelSize = {width: w, height: h};
38623             this.activePanel.setSize(w, h);
38624         }
38625         if(Roo.isIE && this.tabs){
38626             this.tabs.el.repaint();
38627         }
38628     },
38629
38630     /**
38631      * Returns the container element for this region.
38632      * @return {Roo.Element}
38633      */
38634     getEl : function(){
38635         return this.el;
38636     },
38637
38638     /**
38639      * Hides this region.
38640      */
38641     hide : function(){
38642         //if(!this.collapsed){
38643             this.el.dom.style.left = "-2000px";
38644             this.el.hide();
38645         //}else{
38646          //   this.collapsedEl.dom.style.left = "-2000px";
38647          //   this.collapsedEl.hide();
38648        // }
38649         this.visible = false;
38650         this.fireEvent("visibilitychange", this, false);
38651     },
38652
38653     /**
38654      * Shows this region if it was previously hidden.
38655      */
38656     show : function(){
38657         //if(!this.collapsed){
38658             this.el.show();
38659         //}else{
38660         //    this.collapsedEl.show();
38661        // }
38662         this.visible = true;
38663         this.fireEvent("visibilitychange", this, true);
38664     },
38665 /*
38666     closeClicked : function(){
38667         if(this.activePanel){
38668             this.remove(this.activePanel);
38669         }
38670     },
38671
38672     collapseClick : function(e){
38673         if(this.isSlid){
38674            e.stopPropagation();
38675            this.slideIn();
38676         }else{
38677            e.stopPropagation();
38678            this.slideOut();
38679         }
38680     },
38681 */
38682     /**
38683      * Collapses this region.
38684      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38685      */
38686     /*
38687     collapse : function(skipAnim, skipCheck = false){
38688         if(this.collapsed) {
38689             return;
38690         }
38691         
38692         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38693             
38694             this.collapsed = true;
38695             if(this.split){
38696                 this.split.el.hide();
38697             }
38698             if(this.config.animate && skipAnim !== true){
38699                 this.fireEvent("invalidated", this);
38700                 this.animateCollapse();
38701             }else{
38702                 this.el.setLocation(-20000,-20000);
38703                 this.el.hide();
38704                 this.collapsedEl.show();
38705                 this.fireEvent("collapsed", this);
38706                 this.fireEvent("invalidated", this);
38707             }
38708         }
38709         
38710     },
38711 */
38712     animateCollapse : function(){
38713         // overridden
38714     },
38715
38716     /**
38717      * Expands this region if it was previously collapsed.
38718      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38719      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38720      */
38721     /*
38722     expand : function(e, skipAnim){
38723         if(e) {
38724             e.stopPropagation();
38725         }
38726         if(!this.collapsed || this.el.hasActiveFx()) {
38727             return;
38728         }
38729         if(this.isSlid){
38730             this.afterSlideIn();
38731             skipAnim = true;
38732         }
38733         this.collapsed = false;
38734         if(this.config.animate && skipAnim !== true){
38735             this.animateExpand();
38736         }else{
38737             this.el.show();
38738             if(this.split){
38739                 this.split.el.show();
38740             }
38741             this.collapsedEl.setLocation(-2000,-2000);
38742             this.collapsedEl.hide();
38743             this.fireEvent("invalidated", this);
38744             this.fireEvent("expanded", this);
38745         }
38746     },
38747 */
38748     animateExpand : function(){
38749         // overridden
38750     },
38751
38752     initTabs : function()
38753     {
38754         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38755         
38756         var ts = new Roo.bootstrap.panel.Tabs({
38757             el: this.bodyEl.dom,
38758             region : this,
38759             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38760             disableTooltips: this.config.disableTabTips,
38761             toolbar : this.config.toolbar
38762         });
38763         
38764         if(this.config.hideTabs){
38765             ts.stripWrap.setDisplayed(false);
38766         }
38767         this.tabs = ts;
38768         ts.resizeTabs = this.config.resizeTabs === true;
38769         ts.minTabWidth = this.config.minTabWidth || 40;
38770         ts.maxTabWidth = this.config.maxTabWidth || 250;
38771         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38772         ts.monitorResize = false;
38773         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38774         ts.bodyEl.addClass('roo-layout-tabs-body');
38775         this.panels.each(this.initPanelAsTab, this);
38776     },
38777
38778     initPanelAsTab : function(panel){
38779         var ti = this.tabs.addTab(
38780             panel.getEl().id,
38781             panel.getTitle(),
38782             null,
38783             this.config.closeOnTab && panel.isClosable(),
38784             panel.tpl
38785         );
38786         if(panel.tabTip !== undefined){
38787             ti.setTooltip(panel.tabTip);
38788         }
38789         ti.on("activate", function(){
38790               this.setActivePanel(panel);
38791         }, this);
38792         
38793         if(this.config.closeOnTab){
38794             ti.on("beforeclose", function(t, e){
38795                 e.cancel = true;
38796                 this.remove(panel);
38797             }, this);
38798         }
38799         
38800         panel.tabItem = ti;
38801         
38802         return ti;
38803     },
38804
38805     updatePanelTitle : function(panel, title)
38806     {
38807         if(this.activePanel == panel){
38808             this.updateTitle(title);
38809         }
38810         if(this.tabs){
38811             var ti = this.tabs.getTab(panel.getEl().id);
38812             ti.setText(title);
38813             if(panel.tabTip !== undefined){
38814                 ti.setTooltip(panel.tabTip);
38815             }
38816         }
38817     },
38818
38819     updateTitle : function(title){
38820         if(this.titleTextEl && !this.config.title){
38821             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38822         }
38823     },
38824
38825     setActivePanel : function(panel)
38826     {
38827         panel = this.getPanel(panel);
38828         if(this.activePanel && this.activePanel != panel){
38829             if(this.activePanel.setActiveState(false) === false){
38830                 return;
38831             }
38832         }
38833         this.activePanel = panel;
38834         panel.setActiveState(true);
38835         if(this.panelSize){
38836             panel.setSize(this.panelSize.width, this.panelSize.height);
38837         }
38838         if(this.closeBtn){
38839             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38840         }
38841         this.updateTitle(panel.getTitle());
38842         if(this.tabs){
38843             this.fireEvent("invalidated", this);
38844         }
38845         this.fireEvent("panelactivated", this, panel);
38846     },
38847
38848     /**
38849      * Shows the specified panel.
38850      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38851      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38852      */
38853     showPanel : function(panel)
38854     {
38855         panel = this.getPanel(panel);
38856         if(panel){
38857             if(this.tabs){
38858                 var tab = this.tabs.getTab(panel.getEl().id);
38859                 if(tab.isHidden()){
38860                     this.tabs.unhideTab(tab.id);
38861                 }
38862                 tab.activate();
38863             }else{
38864                 this.setActivePanel(panel);
38865             }
38866         }
38867         return panel;
38868     },
38869
38870     /**
38871      * Get the active panel for this region.
38872      * @return {Roo.ContentPanel} The active panel or null
38873      */
38874     getActivePanel : function(){
38875         return this.activePanel;
38876     },
38877
38878     validateVisibility : function(){
38879         if(this.panels.getCount() < 1){
38880             this.updateTitle("&#160;");
38881             this.closeBtn.hide();
38882             this.hide();
38883         }else{
38884             if(!this.isVisible()){
38885                 this.show();
38886             }
38887         }
38888     },
38889
38890     /**
38891      * Adds the passed ContentPanel(s) to this region.
38892      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38893      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38894      */
38895     add : function(panel)
38896     {
38897         if(arguments.length > 1){
38898             for(var i = 0, len = arguments.length; i < len; i++) {
38899                 this.add(arguments[i]);
38900             }
38901             return null;
38902         }
38903         
38904         // if we have not been rendered yet, then we can not really do much of this..
38905         if (!this.bodyEl) {
38906             this.unrendered_panels.push(panel);
38907             return panel;
38908         }
38909         
38910         
38911         
38912         
38913         if(this.hasPanel(panel)){
38914             this.showPanel(panel);
38915             return panel;
38916         }
38917         panel.setRegion(this);
38918         this.panels.add(panel);
38919        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38920             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38921             // and hide them... ???
38922             this.bodyEl.dom.appendChild(panel.getEl().dom);
38923             if(panel.background !== true){
38924                 this.setActivePanel(panel);
38925             }
38926             this.fireEvent("paneladded", this, panel);
38927             return panel;
38928         }
38929         */
38930         if(!this.tabs){
38931             this.initTabs();
38932         }else{
38933             this.initPanelAsTab(panel);
38934         }
38935         
38936         
38937         if(panel.background !== true){
38938             this.tabs.activate(panel.getEl().id);
38939         }
38940         this.fireEvent("paneladded", this, panel);
38941         return panel;
38942     },
38943
38944     /**
38945      * Hides the tab for the specified panel.
38946      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38947      */
38948     hidePanel : function(panel){
38949         if(this.tabs && (panel = this.getPanel(panel))){
38950             this.tabs.hideTab(panel.getEl().id);
38951         }
38952     },
38953
38954     /**
38955      * Unhides the tab for a previously hidden panel.
38956      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38957      */
38958     unhidePanel : function(panel){
38959         if(this.tabs && (panel = this.getPanel(panel))){
38960             this.tabs.unhideTab(panel.getEl().id);
38961         }
38962     },
38963
38964     clearPanels : function(){
38965         while(this.panels.getCount() > 0){
38966              this.remove(this.panels.first());
38967         }
38968     },
38969
38970     /**
38971      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38972      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38973      * @param {Boolean} preservePanel Overrides the config preservePanel option
38974      * @return {Roo.ContentPanel} The panel that was removed
38975      */
38976     remove : function(panel, preservePanel)
38977     {
38978         panel = this.getPanel(panel);
38979         if(!panel){
38980             return null;
38981         }
38982         var e = {};
38983         this.fireEvent("beforeremove", this, panel, e);
38984         if(e.cancel === true){
38985             return null;
38986         }
38987         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38988         var panelId = panel.getId();
38989         this.panels.removeKey(panelId);
38990         if(preservePanel){
38991             document.body.appendChild(panel.getEl().dom);
38992         }
38993         if(this.tabs){
38994             this.tabs.removeTab(panel.getEl().id);
38995         }else if (!preservePanel){
38996             this.bodyEl.dom.removeChild(panel.getEl().dom);
38997         }
38998         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38999             var p = this.panels.first();
39000             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39001             tempEl.appendChild(p.getEl().dom);
39002             this.bodyEl.update("");
39003             this.bodyEl.dom.appendChild(p.getEl().dom);
39004             tempEl = null;
39005             this.updateTitle(p.getTitle());
39006             this.tabs = null;
39007             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39008             this.setActivePanel(p);
39009         }
39010         panel.setRegion(null);
39011         if(this.activePanel == panel){
39012             this.activePanel = null;
39013         }
39014         if(this.config.autoDestroy !== false && preservePanel !== true){
39015             try{panel.destroy();}catch(e){}
39016         }
39017         this.fireEvent("panelremoved", this, panel);
39018         return panel;
39019     },
39020
39021     /**
39022      * Returns the TabPanel component used by this region
39023      * @return {Roo.TabPanel}
39024      */
39025     getTabs : function(){
39026         return this.tabs;
39027     },
39028
39029     createTool : function(parentEl, className){
39030         var btn = Roo.DomHelper.append(parentEl, {
39031             tag: "div",
39032             cls: "x-layout-tools-button",
39033             children: [ {
39034                 tag: "div",
39035                 cls: "roo-layout-tools-button-inner " + className,
39036                 html: "&#160;"
39037             }]
39038         }, true);
39039         btn.addClassOnOver("roo-layout-tools-button-over");
39040         return btn;
39041     }
39042 });/*
39043  * Based on:
39044  * Ext JS Library 1.1.1
39045  * Copyright(c) 2006-2007, Ext JS, LLC.
39046  *
39047  * Originally Released Under LGPL - original licence link has changed is not relivant.
39048  *
39049  * Fork - LGPL
39050  * <script type="text/javascript">
39051  */
39052  
39053
39054
39055 /**
39056  * @class Roo.SplitLayoutRegion
39057  * @extends Roo.LayoutRegion
39058  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39059  */
39060 Roo.bootstrap.layout.Split = function(config){
39061     this.cursor = config.cursor;
39062     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39063 };
39064
39065 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39066 {
39067     splitTip : "Drag to resize.",
39068     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39069     useSplitTips : false,
39070
39071     applyConfig : function(config){
39072         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39073     },
39074     
39075     onRender : function(ctr,pos) {
39076         
39077         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39078         if(!this.config.split){
39079             return;
39080         }
39081         if(!this.split){
39082             
39083             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39084                             tag: "div",
39085                             id: this.el.id + "-split",
39086                             cls: "roo-layout-split roo-layout-split-"+this.position,
39087                             html: "&#160;"
39088             });
39089             /** The SplitBar for this region 
39090             * @type Roo.SplitBar */
39091             // does not exist yet...
39092             Roo.log([this.position, this.orientation]);
39093             
39094             this.split = new Roo.bootstrap.SplitBar({
39095                 dragElement : splitEl,
39096                 resizingElement: this.el,
39097                 orientation : this.orientation
39098             });
39099             
39100             this.split.on("moved", this.onSplitMove, this);
39101             this.split.useShim = this.config.useShim === true;
39102             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39103             if(this.useSplitTips){
39104                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39105             }
39106             //if(config.collapsible){
39107             //    this.split.el.on("dblclick", this.collapse,  this);
39108             //}
39109         }
39110         if(typeof this.config.minSize != "undefined"){
39111             this.split.minSize = this.config.minSize;
39112         }
39113         if(typeof this.config.maxSize != "undefined"){
39114             this.split.maxSize = this.config.maxSize;
39115         }
39116         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39117             this.hideSplitter();
39118         }
39119         
39120     },
39121
39122     getHMaxSize : function(){
39123          var cmax = this.config.maxSize || 10000;
39124          var center = this.mgr.getRegion("center");
39125          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39126     },
39127
39128     getVMaxSize : function(){
39129          var cmax = this.config.maxSize || 10000;
39130          var center = this.mgr.getRegion("center");
39131          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39132     },
39133
39134     onSplitMove : function(split, newSize){
39135         this.fireEvent("resized", this, newSize);
39136     },
39137     
39138     /** 
39139      * Returns the {@link Roo.SplitBar} for this region.
39140      * @return {Roo.SplitBar}
39141      */
39142     getSplitBar : function(){
39143         return this.split;
39144     },
39145     
39146     hide : function(){
39147         this.hideSplitter();
39148         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39149     },
39150
39151     hideSplitter : function(){
39152         if(this.split){
39153             this.split.el.setLocation(-2000,-2000);
39154             this.split.el.hide();
39155         }
39156     },
39157
39158     show : function(){
39159         if(this.split){
39160             this.split.el.show();
39161         }
39162         Roo.bootstrap.layout.Split.superclass.show.call(this);
39163     },
39164     
39165     beforeSlide: function(){
39166         if(Roo.isGecko){// firefox overflow auto bug workaround
39167             this.bodyEl.clip();
39168             if(this.tabs) {
39169                 this.tabs.bodyEl.clip();
39170             }
39171             if(this.activePanel){
39172                 this.activePanel.getEl().clip();
39173                 
39174                 if(this.activePanel.beforeSlide){
39175                     this.activePanel.beforeSlide();
39176                 }
39177             }
39178         }
39179     },
39180     
39181     afterSlide : function(){
39182         if(Roo.isGecko){// firefox overflow auto bug workaround
39183             this.bodyEl.unclip();
39184             if(this.tabs) {
39185                 this.tabs.bodyEl.unclip();
39186             }
39187             if(this.activePanel){
39188                 this.activePanel.getEl().unclip();
39189                 if(this.activePanel.afterSlide){
39190                     this.activePanel.afterSlide();
39191                 }
39192             }
39193         }
39194     },
39195
39196     initAutoHide : function(){
39197         if(this.autoHide !== false){
39198             if(!this.autoHideHd){
39199                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39200                 this.autoHideHd = {
39201                     "mouseout": function(e){
39202                         if(!e.within(this.el, true)){
39203                             st.delay(500);
39204                         }
39205                     },
39206                     "mouseover" : function(e){
39207                         st.cancel();
39208                     },
39209                     scope : this
39210                 };
39211             }
39212             this.el.on(this.autoHideHd);
39213         }
39214     },
39215
39216     clearAutoHide : function(){
39217         if(this.autoHide !== false){
39218             this.el.un("mouseout", this.autoHideHd.mouseout);
39219             this.el.un("mouseover", this.autoHideHd.mouseover);
39220         }
39221     },
39222
39223     clearMonitor : function(){
39224         Roo.get(document).un("click", this.slideInIf, this);
39225     },
39226
39227     // these names are backwards but not changed for compat
39228     slideOut : function(){
39229         if(this.isSlid || this.el.hasActiveFx()){
39230             return;
39231         }
39232         this.isSlid = true;
39233         if(this.collapseBtn){
39234             this.collapseBtn.hide();
39235         }
39236         this.closeBtnState = this.closeBtn.getStyle('display');
39237         this.closeBtn.hide();
39238         if(this.stickBtn){
39239             this.stickBtn.show();
39240         }
39241         this.el.show();
39242         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39243         this.beforeSlide();
39244         this.el.setStyle("z-index", 10001);
39245         this.el.slideIn(this.getSlideAnchor(), {
39246             callback: function(){
39247                 this.afterSlide();
39248                 this.initAutoHide();
39249                 Roo.get(document).on("click", this.slideInIf, this);
39250                 this.fireEvent("slideshow", this);
39251             },
39252             scope: this,
39253             block: true
39254         });
39255     },
39256
39257     afterSlideIn : function(){
39258         this.clearAutoHide();
39259         this.isSlid = false;
39260         this.clearMonitor();
39261         this.el.setStyle("z-index", "");
39262         if(this.collapseBtn){
39263             this.collapseBtn.show();
39264         }
39265         this.closeBtn.setStyle('display', this.closeBtnState);
39266         if(this.stickBtn){
39267             this.stickBtn.hide();
39268         }
39269         this.fireEvent("slidehide", this);
39270     },
39271
39272     slideIn : function(cb){
39273         if(!this.isSlid || this.el.hasActiveFx()){
39274             Roo.callback(cb);
39275             return;
39276         }
39277         this.isSlid = false;
39278         this.beforeSlide();
39279         this.el.slideOut(this.getSlideAnchor(), {
39280             callback: function(){
39281                 this.el.setLeftTop(-10000, -10000);
39282                 this.afterSlide();
39283                 this.afterSlideIn();
39284                 Roo.callback(cb);
39285             },
39286             scope: this,
39287             block: true
39288         });
39289     },
39290     
39291     slideInIf : function(e){
39292         if(!e.within(this.el)){
39293             this.slideIn();
39294         }
39295     },
39296
39297     animateCollapse : function(){
39298         this.beforeSlide();
39299         this.el.setStyle("z-index", 20000);
39300         var anchor = this.getSlideAnchor();
39301         this.el.slideOut(anchor, {
39302             callback : function(){
39303                 this.el.setStyle("z-index", "");
39304                 this.collapsedEl.slideIn(anchor, {duration:.3});
39305                 this.afterSlide();
39306                 this.el.setLocation(-10000,-10000);
39307                 this.el.hide();
39308                 this.fireEvent("collapsed", this);
39309             },
39310             scope: this,
39311             block: true
39312         });
39313     },
39314
39315     animateExpand : function(){
39316         this.beforeSlide();
39317         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39318         this.el.setStyle("z-index", 20000);
39319         this.collapsedEl.hide({
39320             duration:.1
39321         });
39322         this.el.slideIn(this.getSlideAnchor(), {
39323             callback : function(){
39324                 this.el.setStyle("z-index", "");
39325                 this.afterSlide();
39326                 if(this.split){
39327                     this.split.el.show();
39328                 }
39329                 this.fireEvent("invalidated", this);
39330                 this.fireEvent("expanded", this);
39331             },
39332             scope: this,
39333             block: true
39334         });
39335     },
39336
39337     anchors : {
39338         "west" : "left",
39339         "east" : "right",
39340         "north" : "top",
39341         "south" : "bottom"
39342     },
39343
39344     sanchors : {
39345         "west" : "l",
39346         "east" : "r",
39347         "north" : "t",
39348         "south" : "b"
39349     },
39350
39351     canchors : {
39352         "west" : "tl-tr",
39353         "east" : "tr-tl",
39354         "north" : "tl-bl",
39355         "south" : "bl-tl"
39356     },
39357
39358     getAnchor : function(){
39359         return this.anchors[this.position];
39360     },
39361
39362     getCollapseAnchor : function(){
39363         return this.canchors[this.position];
39364     },
39365
39366     getSlideAnchor : function(){
39367         return this.sanchors[this.position];
39368     },
39369
39370     getAlignAdj : function(){
39371         var cm = this.cmargins;
39372         switch(this.position){
39373             case "west":
39374                 return [0, 0];
39375             break;
39376             case "east":
39377                 return [0, 0];
39378             break;
39379             case "north":
39380                 return [0, 0];
39381             break;
39382             case "south":
39383                 return [0, 0];
39384             break;
39385         }
39386     },
39387
39388     getExpandAdj : function(){
39389         var c = this.collapsedEl, cm = this.cmargins;
39390         switch(this.position){
39391             case "west":
39392                 return [-(cm.right+c.getWidth()+cm.left), 0];
39393             break;
39394             case "east":
39395                 return [cm.right+c.getWidth()+cm.left, 0];
39396             break;
39397             case "north":
39398                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39399             break;
39400             case "south":
39401                 return [0, cm.top+cm.bottom+c.getHeight()];
39402             break;
39403         }
39404     }
39405 });/*
39406  * Based on:
39407  * Ext JS Library 1.1.1
39408  * Copyright(c) 2006-2007, Ext JS, LLC.
39409  *
39410  * Originally Released Under LGPL - original licence link has changed is not relivant.
39411  *
39412  * Fork - LGPL
39413  * <script type="text/javascript">
39414  */
39415 /*
39416  * These classes are private internal classes
39417  */
39418 Roo.bootstrap.layout.Center = function(config){
39419     config.region = "center";
39420     Roo.bootstrap.layout.Region.call(this, config);
39421     this.visible = true;
39422     this.minWidth = config.minWidth || 20;
39423     this.minHeight = config.minHeight || 20;
39424 };
39425
39426 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39427     hide : function(){
39428         // center panel can't be hidden
39429     },
39430     
39431     show : function(){
39432         // center panel can't be hidden
39433     },
39434     
39435     getMinWidth: function(){
39436         return this.minWidth;
39437     },
39438     
39439     getMinHeight: function(){
39440         return this.minHeight;
39441     }
39442 });
39443
39444
39445
39446
39447  
39448
39449
39450
39451
39452
39453
39454 Roo.bootstrap.layout.North = function(config)
39455 {
39456     config.region = 'north';
39457     config.cursor = 'n-resize';
39458     
39459     Roo.bootstrap.layout.Split.call(this, config);
39460     
39461     
39462     if(this.split){
39463         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39464         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39465         this.split.el.addClass("roo-layout-split-v");
39466     }
39467     //var size = config.initialSize || config.height;
39468     //if(this.el && typeof size != "undefined"){
39469     //    this.el.setHeight(size);
39470     //}
39471 };
39472 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39473 {
39474     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39475      
39476      
39477     onRender : function(ctr, pos)
39478     {
39479         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39480         var size = this.config.initialSize || this.config.height;
39481         if(this.el && typeof size != "undefined"){
39482             this.el.setHeight(size);
39483         }
39484     
39485     },
39486     
39487     getBox : function(){
39488         if(this.collapsed){
39489             return this.collapsedEl.getBox();
39490         }
39491         var box = this.el.getBox();
39492         if(this.split){
39493             box.height += this.split.el.getHeight();
39494         }
39495         return box;
39496     },
39497     
39498     updateBox : function(box){
39499         if(this.split && !this.collapsed){
39500             box.height -= this.split.el.getHeight();
39501             this.split.el.setLeft(box.x);
39502             this.split.el.setTop(box.y+box.height);
39503             this.split.el.setWidth(box.width);
39504         }
39505         if(this.collapsed){
39506             this.updateBody(box.width, null);
39507         }
39508         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39509     }
39510 });
39511
39512
39513
39514
39515
39516 Roo.bootstrap.layout.South = function(config){
39517     config.region = 'south';
39518     config.cursor = 's-resize';
39519     Roo.bootstrap.layout.Split.call(this, config);
39520     if(this.split){
39521         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39522         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39523         this.split.el.addClass("roo-layout-split-v");
39524     }
39525     
39526 };
39527
39528 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39529     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39530     
39531     onRender : function(ctr, pos)
39532     {
39533         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39534         var size = this.config.initialSize || this.config.height;
39535         if(this.el && typeof size != "undefined"){
39536             this.el.setHeight(size);
39537         }
39538     
39539     },
39540     
39541     getBox : function(){
39542         if(this.collapsed){
39543             return this.collapsedEl.getBox();
39544         }
39545         var box = this.el.getBox();
39546         if(this.split){
39547             var sh = this.split.el.getHeight();
39548             box.height += sh;
39549             box.y -= sh;
39550         }
39551         return box;
39552     },
39553     
39554     updateBox : function(box){
39555         if(this.split && !this.collapsed){
39556             var sh = this.split.el.getHeight();
39557             box.height -= sh;
39558             box.y += sh;
39559             this.split.el.setLeft(box.x);
39560             this.split.el.setTop(box.y-sh);
39561             this.split.el.setWidth(box.width);
39562         }
39563         if(this.collapsed){
39564             this.updateBody(box.width, null);
39565         }
39566         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39567     }
39568 });
39569
39570 Roo.bootstrap.layout.East = function(config){
39571     config.region = "east";
39572     config.cursor = "e-resize";
39573     Roo.bootstrap.layout.Split.call(this, config);
39574     if(this.split){
39575         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39576         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39577         this.split.el.addClass("roo-layout-split-h");
39578     }
39579     
39580 };
39581 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39582     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39583     
39584     onRender : function(ctr, pos)
39585     {
39586         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39587         var size = this.config.initialSize || this.config.width;
39588         if(this.el && typeof size != "undefined"){
39589             this.el.setWidth(size);
39590         }
39591     
39592     },
39593     
39594     getBox : function(){
39595         if(this.collapsed){
39596             return this.collapsedEl.getBox();
39597         }
39598         var box = this.el.getBox();
39599         if(this.split){
39600             var sw = this.split.el.getWidth();
39601             box.width += sw;
39602             box.x -= sw;
39603         }
39604         return box;
39605     },
39606
39607     updateBox : function(box){
39608         if(this.split && !this.collapsed){
39609             var sw = this.split.el.getWidth();
39610             box.width -= sw;
39611             this.split.el.setLeft(box.x);
39612             this.split.el.setTop(box.y);
39613             this.split.el.setHeight(box.height);
39614             box.x += sw;
39615         }
39616         if(this.collapsed){
39617             this.updateBody(null, box.height);
39618         }
39619         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39620     }
39621 });
39622
39623 Roo.bootstrap.layout.West = function(config){
39624     config.region = "west";
39625     config.cursor = "w-resize";
39626     
39627     Roo.bootstrap.layout.Split.call(this, config);
39628     if(this.split){
39629         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39630         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39631         this.split.el.addClass("roo-layout-split-h");
39632     }
39633     
39634 };
39635 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39636     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39637     
39638     onRender: function(ctr, pos)
39639     {
39640         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39641         var size = this.config.initialSize || this.config.width;
39642         if(typeof size != "undefined"){
39643             this.el.setWidth(size);
39644         }
39645     },
39646     
39647     getBox : function(){
39648         if(this.collapsed){
39649             return this.collapsedEl.getBox();
39650         }
39651         var box = this.el.getBox();
39652         if (box.width == 0) {
39653             box.width = this.config.width; // kludge?
39654         }
39655         if(this.split){
39656             box.width += this.split.el.getWidth();
39657         }
39658         return box;
39659     },
39660     
39661     updateBox : function(box){
39662         if(this.split && !this.collapsed){
39663             var sw = this.split.el.getWidth();
39664             box.width -= sw;
39665             this.split.el.setLeft(box.x+box.width);
39666             this.split.el.setTop(box.y);
39667             this.split.el.setHeight(box.height);
39668         }
39669         if(this.collapsed){
39670             this.updateBody(null, box.height);
39671         }
39672         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39673     }
39674 });Roo.namespace("Roo.bootstrap.panel");/*
39675  * Based on:
39676  * Ext JS Library 1.1.1
39677  * Copyright(c) 2006-2007, Ext JS, LLC.
39678  *
39679  * Originally Released Under LGPL - original licence link has changed is not relivant.
39680  *
39681  * Fork - LGPL
39682  * <script type="text/javascript">
39683  */
39684 /**
39685  * @class Roo.ContentPanel
39686  * @extends Roo.util.Observable
39687  * A basic ContentPanel element.
39688  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39689  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39690  * @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
39691  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39692  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39693  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39694  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39695  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39696  * @cfg {String} title          The title for this panel
39697  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39698  * @cfg {String} url            Calls {@link #setUrl} with this value
39699  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39700  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39701  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39702  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39703  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39704  * @cfg {Boolean} badges render the badges
39705  * @cfg {String} cls  extra classes to use  
39706  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39707
39708  * @constructor
39709  * Create a new ContentPanel.
39710  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39711  * @param {String/Object} config A string to set only the title or a config object
39712  * @param {String} content (optional) Set the HTML content for this panel
39713  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39714  */
39715 Roo.bootstrap.panel.Content = function( config){
39716     
39717     this.tpl = config.tpl || false;
39718     
39719     var el = config.el;
39720     var content = config.content;
39721
39722     if(config.autoCreate){ // xtype is available if this is called from factory
39723         el = Roo.id();
39724     }
39725     this.el = Roo.get(el);
39726     if(!this.el && config && config.autoCreate){
39727         if(typeof config.autoCreate == "object"){
39728             if(!config.autoCreate.id){
39729                 config.autoCreate.id = config.id||el;
39730             }
39731             this.el = Roo.DomHelper.append(document.body,
39732                         config.autoCreate, true);
39733         }else{
39734             var elcfg =  {
39735                 tag: "div",
39736                 cls: (config.cls || '') +
39737                     (config.background ? ' bg-' + config.background : '') +
39738                     " roo-layout-inactive-content",
39739                 id: config.id||el
39740             };
39741             if (config.iframe) {
39742                 elcfg.cn = [
39743                     {
39744                         tag : 'iframe',
39745                         style : 'border: 0px',
39746                         src : 'about:blank'
39747                     }
39748                 ];
39749             }
39750               
39751             if (config.html) {
39752                 elcfg.html = config.html;
39753                 
39754             }
39755                         
39756             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39757             if (config.iframe) {
39758                 this.iframeEl = this.el.select('iframe',true).first();
39759             }
39760             
39761         }
39762     } 
39763     this.closable = false;
39764     this.loaded = false;
39765     this.active = false;
39766    
39767       
39768     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39769         
39770         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39771         
39772         this.wrapEl = this.el; //this.el.wrap();
39773         var ti = [];
39774         if (config.toolbar.items) {
39775             ti = config.toolbar.items ;
39776             delete config.toolbar.items ;
39777         }
39778         
39779         var nitems = [];
39780         this.toolbar.render(this.wrapEl, 'before');
39781         for(var i =0;i < ti.length;i++) {
39782           //  Roo.log(['add child', items[i]]);
39783             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39784         }
39785         this.toolbar.items = nitems;
39786         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39787         delete config.toolbar;
39788         
39789     }
39790     /*
39791     // xtype created footer. - not sure if will work as we normally have to render first..
39792     if (this.footer && !this.footer.el && this.footer.xtype) {
39793         if (!this.wrapEl) {
39794             this.wrapEl = this.el.wrap();
39795         }
39796     
39797         this.footer.container = this.wrapEl.createChild();
39798          
39799         this.footer = Roo.factory(this.footer, Roo);
39800         
39801     }
39802     */
39803     
39804      if(typeof config == "string"){
39805         this.title = config;
39806     }else{
39807         Roo.apply(this, config);
39808     }
39809     
39810     if(this.resizeEl){
39811         this.resizeEl = Roo.get(this.resizeEl, true);
39812     }else{
39813         this.resizeEl = this.el;
39814     }
39815     // handle view.xtype
39816     
39817  
39818     
39819     
39820     this.addEvents({
39821         /**
39822          * @event activate
39823          * Fires when this panel is activated. 
39824          * @param {Roo.ContentPanel} this
39825          */
39826         "activate" : true,
39827         /**
39828          * @event deactivate
39829          * Fires when this panel is activated. 
39830          * @param {Roo.ContentPanel} this
39831          */
39832         "deactivate" : true,
39833
39834         /**
39835          * @event resize
39836          * Fires when this panel is resized if fitToFrame is true.
39837          * @param {Roo.ContentPanel} this
39838          * @param {Number} width The width after any component adjustments
39839          * @param {Number} height The height after any component adjustments
39840          */
39841         "resize" : true,
39842         
39843          /**
39844          * @event render
39845          * Fires when this tab is created
39846          * @param {Roo.ContentPanel} this
39847          */
39848         "render" : true
39849         
39850         
39851         
39852     });
39853     
39854
39855     
39856     
39857     if(this.autoScroll && !this.iframe){
39858         this.resizeEl.setStyle("overflow", "auto");
39859     } else {
39860         // fix randome scrolling
39861         //this.el.on('scroll', function() {
39862         //    Roo.log('fix random scolling');
39863         //    this.scrollTo('top',0); 
39864         //});
39865     }
39866     content = content || this.content;
39867     if(content){
39868         this.setContent(content);
39869     }
39870     if(config && config.url){
39871         this.setUrl(this.url, this.params, this.loadOnce);
39872     }
39873     
39874     
39875     
39876     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39877     
39878     if (this.view && typeof(this.view.xtype) != 'undefined') {
39879         this.view.el = this.el.appendChild(document.createElement("div"));
39880         this.view = Roo.factory(this.view); 
39881         this.view.render  &&  this.view.render(false, '');  
39882     }
39883     
39884     
39885     this.fireEvent('render', this);
39886 };
39887
39888 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39889     
39890     cls : '',
39891     background : '',
39892     
39893     tabTip : '',
39894     
39895     iframe : false,
39896     iframeEl : false,
39897     
39898     setRegion : function(region){
39899         this.region = region;
39900         this.setActiveClass(region && !this.background);
39901     },
39902     
39903     
39904     setActiveClass: function(state)
39905     {
39906         if(state){
39907            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39908            this.el.setStyle('position','relative');
39909         }else{
39910            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39911            this.el.setStyle('position', 'absolute');
39912         } 
39913     },
39914     
39915     /**
39916      * Returns the toolbar for this Panel if one was configured. 
39917      * @return {Roo.Toolbar} 
39918      */
39919     getToolbar : function(){
39920         return this.toolbar;
39921     },
39922     
39923     setActiveState : function(active)
39924     {
39925         this.active = active;
39926         this.setActiveClass(active);
39927         if(!active){
39928             if(this.fireEvent("deactivate", this) === false){
39929                 return false;
39930             }
39931             return true;
39932         }
39933         this.fireEvent("activate", this);
39934         return true;
39935     },
39936     /**
39937      * Updates this panel's element (not for iframe)
39938      * @param {String} content The new content
39939      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39940     */
39941     setContent : function(content, loadScripts){
39942         if (this.iframe) {
39943             return;
39944         }
39945         
39946         this.el.update(content, loadScripts);
39947     },
39948
39949     ignoreResize : function(w, h){
39950         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39951             return true;
39952         }else{
39953             this.lastSize = {width: w, height: h};
39954             return false;
39955         }
39956     },
39957     /**
39958      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39959      * @return {Roo.UpdateManager} The UpdateManager
39960      */
39961     getUpdateManager : function(){
39962         if (this.iframe) {
39963             return false;
39964         }
39965         return this.el.getUpdateManager();
39966     },
39967      /**
39968      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39969      * Does not work with IFRAME contents
39970      * @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:
39971 <pre><code>
39972 panel.load({
39973     url: "your-url.php",
39974     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39975     callback: yourFunction,
39976     scope: yourObject, //(optional scope)
39977     discardUrl: false,
39978     nocache: false,
39979     text: "Loading...",
39980     timeout: 30,
39981     scripts: false
39982 });
39983 </code></pre>
39984      
39985      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39986      * 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.
39987      * @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}
39988      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39989      * @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.
39990      * @return {Roo.ContentPanel} this
39991      */
39992     load : function(){
39993         
39994         if (this.iframe) {
39995             return this;
39996         }
39997         
39998         var um = this.el.getUpdateManager();
39999         um.update.apply(um, arguments);
40000         return this;
40001     },
40002
40003
40004     /**
40005      * 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.
40006      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40007      * @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)
40008      * @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)
40009      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40010      */
40011     setUrl : function(url, params, loadOnce){
40012         if (this.iframe) {
40013             this.iframeEl.dom.src = url;
40014             return false;
40015         }
40016         
40017         if(this.refreshDelegate){
40018             this.removeListener("activate", this.refreshDelegate);
40019         }
40020         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40021         this.on("activate", this.refreshDelegate);
40022         return this.el.getUpdateManager();
40023     },
40024     
40025     _handleRefresh : function(url, params, loadOnce){
40026         if(!loadOnce || !this.loaded){
40027             var updater = this.el.getUpdateManager();
40028             updater.update(url, params, this._setLoaded.createDelegate(this));
40029         }
40030     },
40031     
40032     _setLoaded : function(){
40033         this.loaded = true;
40034     }, 
40035     
40036     /**
40037      * Returns this panel's id
40038      * @return {String} 
40039      */
40040     getId : function(){
40041         return this.el.id;
40042     },
40043     
40044     /** 
40045      * Returns this panel's element - used by regiosn to add.
40046      * @return {Roo.Element} 
40047      */
40048     getEl : function(){
40049         return this.wrapEl || this.el;
40050     },
40051     
40052    
40053     
40054     adjustForComponents : function(width, height)
40055     {
40056         //Roo.log('adjustForComponents ');
40057         if(this.resizeEl != this.el){
40058             width -= this.el.getFrameWidth('lr');
40059             height -= this.el.getFrameWidth('tb');
40060         }
40061         if(this.toolbar){
40062             var te = this.toolbar.getEl();
40063             te.setWidth(width);
40064             height -= te.getHeight();
40065         }
40066         if(this.footer){
40067             var te = this.footer.getEl();
40068             te.setWidth(width);
40069             height -= te.getHeight();
40070         }
40071         
40072         
40073         if(this.adjustments){
40074             width += this.adjustments[0];
40075             height += this.adjustments[1];
40076         }
40077         return {"width": width, "height": height};
40078     },
40079     
40080     setSize : function(width, height){
40081         if(this.fitToFrame && !this.ignoreResize(width, height)){
40082             if(this.fitContainer && this.resizeEl != this.el){
40083                 this.el.setSize(width, height);
40084             }
40085             var size = this.adjustForComponents(width, height);
40086             if (this.iframe) {
40087                 this.iframeEl.setSize(width,height);
40088             }
40089             
40090             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40091             this.fireEvent('resize', this, size.width, size.height);
40092             
40093             
40094         }
40095     },
40096     
40097     /**
40098      * Returns this panel's title
40099      * @return {String} 
40100      */
40101     getTitle : function(){
40102         
40103         if (typeof(this.title) != 'object') {
40104             return this.title;
40105         }
40106         
40107         var t = '';
40108         for (var k in this.title) {
40109             if (!this.title.hasOwnProperty(k)) {
40110                 continue;
40111             }
40112             
40113             if (k.indexOf('-') >= 0) {
40114                 var s = k.split('-');
40115                 for (var i = 0; i<s.length; i++) {
40116                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40117                 }
40118             } else {
40119                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40120             }
40121         }
40122         return t;
40123     },
40124     
40125     /**
40126      * Set this panel's title
40127      * @param {String} title
40128      */
40129     setTitle : function(title){
40130         this.title = title;
40131         if(this.region){
40132             this.region.updatePanelTitle(this, title);
40133         }
40134     },
40135     
40136     /**
40137      * Returns true is this panel was configured to be closable
40138      * @return {Boolean} 
40139      */
40140     isClosable : function(){
40141         return this.closable;
40142     },
40143     
40144     beforeSlide : function(){
40145         this.el.clip();
40146         this.resizeEl.clip();
40147     },
40148     
40149     afterSlide : function(){
40150         this.el.unclip();
40151         this.resizeEl.unclip();
40152     },
40153     
40154     /**
40155      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40156      *   Will fail silently if the {@link #setUrl} method has not been called.
40157      *   This does not activate the panel, just updates its content.
40158      */
40159     refresh : function(){
40160         if(this.refreshDelegate){
40161            this.loaded = false;
40162            this.refreshDelegate();
40163         }
40164     },
40165     
40166     /**
40167      * Destroys this panel
40168      */
40169     destroy : function(){
40170         this.el.removeAllListeners();
40171         var tempEl = document.createElement("span");
40172         tempEl.appendChild(this.el.dom);
40173         tempEl.innerHTML = "";
40174         this.el.remove();
40175         this.el = null;
40176     },
40177     
40178     /**
40179      * form - if the content panel contains a form - this is a reference to it.
40180      * @type {Roo.form.Form}
40181      */
40182     form : false,
40183     /**
40184      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40185      *    This contains a reference to it.
40186      * @type {Roo.View}
40187      */
40188     view : false,
40189     
40190       /**
40191      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40192      * <pre><code>
40193
40194 layout.addxtype({
40195        xtype : 'Form',
40196        items: [ .... ]
40197    }
40198 );
40199
40200 </code></pre>
40201      * @param {Object} cfg Xtype definition of item to add.
40202      */
40203     
40204     
40205     getChildContainer: function () {
40206         return this.getEl();
40207     }
40208     
40209     
40210     /*
40211         var  ret = new Roo.factory(cfg);
40212         return ret;
40213         
40214         
40215         // add form..
40216         if (cfg.xtype.match(/^Form$/)) {
40217             
40218             var el;
40219             //if (this.footer) {
40220             //    el = this.footer.container.insertSibling(false, 'before');
40221             //} else {
40222                 el = this.el.createChild();
40223             //}
40224
40225             this.form = new  Roo.form.Form(cfg);
40226             
40227             
40228             if ( this.form.allItems.length) {
40229                 this.form.render(el.dom);
40230             }
40231             return this.form;
40232         }
40233         // should only have one of theses..
40234         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40235             // views.. should not be just added - used named prop 'view''
40236             
40237             cfg.el = this.el.appendChild(document.createElement("div"));
40238             // factory?
40239             
40240             var ret = new Roo.factory(cfg);
40241              
40242              ret.render && ret.render(false, ''); // render blank..
40243             this.view = ret;
40244             return ret;
40245         }
40246         return false;
40247     }
40248     \*/
40249 });
40250  
40251 /**
40252  * @class Roo.bootstrap.panel.Grid
40253  * @extends Roo.bootstrap.panel.Content
40254  * @constructor
40255  * Create a new GridPanel.
40256  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40257  * @param {Object} config A the config object
40258   
40259  */
40260
40261
40262
40263 Roo.bootstrap.panel.Grid = function(config)
40264 {
40265     
40266       
40267     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40268         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40269
40270     config.el = this.wrapper;
40271     //this.el = this.wrapper;
40272     
40273       if (config.container) {
40274         // ctor'ed from a Border/panel.grid
40275         
40276         
40277         this.wrapper.setStyle("overflow", "hidden");
40278         this.wrapper.addClass('roo-grid-container');
40279
40280     }
40281     
40282     
40283     if(config.toolbar){
40284         var tool_el = this.wrapper.createChild();    
40285         this.toolbar = Roo.factory(config.toolbar);
40286         var ti = [];
40287         if (config.toolbar.items) {
40288             ti = config.toolbar.items ;
40289             delete config.toolbar.items ;
40290         }
40291         
40292         var nitems = [];
40293         this.toolbar.render(tool_el);
40294         for(var i =0;i < ti.length;i++) {
40295           //  Roo.log(['add child', items[i]]);
40296             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40297         }
40298         this.toolbar.items = nitems;
40299         
40300         delete config.toolbar;
40301     }
40302     
40303     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40304     config.grid.scrollBody = true;;
40305     config.grid.monitorWindowResize = false; // turn off autosizing
40306     config.grid.autoHeight = false;
40307     config.grid.autoWidth = false;
40308     
40309     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40310     
40311     if (config.background) {
40312         // render grid on panel activation (if panel background)
40313         this.on('activate', function(gp) {
40314             if (!gp.grid.rendered) {
40315                 gp.grid.render(this.wrapper);
40316                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40317             }
40318         });
40319             
40320     } else {
40321         this.grid.render(this.wrapper);
40322         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40323
40324     }
40325     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40326     // ??? needed ??? config.el = this.wrapper;
40327     
40328     
40329     
40330   
40331     // xtype created footer. - not sure if will work as we normally have to render first..
40332     if (this.footer && !this.footer.el && this.footer.xtype) {
40333         
40334         var ctr = this.grid.getView().getFooterPanel(true);
40335         this.footer.dataSource = this.grid.dataSource;
40336         this.footer = Roo.factory(this.footer, Roo);
40337         this.footer.render(ctr);
40338         
40339     }
40340     
40341     
40342     
40343     
40344      
40345 };
40346
40347 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40348     getId : function(){
40349         return this.grid.id;
40350     },
40351     
40352     /**
40353      * Returns the grid for this panel
40354      * @return {Roo.bootstrap.Table} 
40355      */
40356     getGrid : function(){
40357         return this.grid;    
40358     },
40359     
40360     setSize : function(width, height){
40361         if(!this.ignoreResize(width, height)){
40362             var grid = this.grid;
40363             var size = this.adjustForComponents(width, height);
40364             // tfoot is not a footer?
40365           
40366             
40367             var gridel = grid.getGridEl();
40368             gridel.setSize(size.width, size.height);
40369             
40370             var tbd = grid.getGridEl().select('tbody', true).first();
40371             var thd = grid.getGridEl().select('thead',true).first();
40372             var tbf= grid.getGridEl().select('tfoot', true).first();
40373
40374             if (tbf) {
40375                 size.height -= tbf.getHeight();
40376             }
40377             if (thd) {
40378                 size.height -= thd.getHeight();
40379             }
40380             
40381             tbd.setSize(size.width, size.height );
40382             // this is for the account management tab -seems to work there.
40383             var thd = grid.getGridEl().select('thead',true).first();
40384             //if (tbd) {
40385             //    tbd.setSize(size.width, size.height - thd.getHeight());
40386             //}
40387              
40388             grid.autoSize();
40389         }
40390     },
40391      
40392     
40393     
40394     beforeSlide : function(){
40395         this.grid.getView().scroller.clip();
40396     },
40397     
40398     afterSlide : function(){
40399         this.grid.getView().scroller.unclip();
40400     },
40401     
40402     destroy : function(){
40403         this.grid.destroy();
40404         delete this.grid;
40405         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40406     }
40407 });
40408
40409 /**
40410  * @class Roo.bootstrap.panel.Nest
40411  * @extends Roo.bootstrap.panel.Content
40412  * @constructor
40413  * Create a new Panel, that can contain a layout.Border.
40414  * 
40415  * 
40416  * @param {Roo.BorderLayout} layout The layout for this panel
40417  * @param {String/Object} config A string to set only the title or a config object
40418  */
40419 Roo.bootstrap.panel.Nest = function(config)
40420 {
40421     // construct with only one argument..
40422     /* FIXME - implement nicer consturctors
40423     if (layout.layout) {
40424         config = layout;
40425         layout = config.layout;
40426         delete config.layout;
40427     }
40428     if (layout.xtype && !layout.getEl) {
40429         // then layout needs constructing..
40430         layout = Roo.factory(layout, Roo);
40431     }
40432     */
40433     
40434     config.el =  config.layout.getEl();
40435     
40436     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40437     
40438     config.layout.monitorWindowResize = false; // turn off autosizing
40439     this.layout = config.layout;
40440     this.layout.getEl().addClass("roo-layout-nested-layout");
40441     this.layout.parent = this;
40442     
40443     
40444     
40445     
40446 };
40447
40448 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40449
40450     setSize : function(width, height){
40451         if(!this.ignoreResize(width, height)){
40452             var size = this.adjustForComponents(width, height);
40453             var el = this.layout.getEl();
40454             if (size.height < 1) {
40455                 el.setWidth(size.width);   
40456             } else {
40457                 el.setSize(size.width, size.height);
40458             }
40459             var touch = el.dom.offsetWidth;
40460             this.layout.layout();
40461             // ie requires a double layout on the first pass
40462             if(Roo.isIE && !this.initialized){
40463                 this.initialized = true;
40464                 this.layout.layout();
40465             }
40466         }
40467     },
40468     
40469     // activate all subpanels if not currently active..
40470     
40471     setActiveState : function(active){
40472         this.active = active;
40473         this.setActiveClass(active);
40474         
40475         if(!active){
40476             this.fireEvent("deactivate", this);
40477             return;
40478         }
40479         
40480         this.fireEvent("activate", this);
40481         // not sure if this should happen before or after..
40482         if (!this.layout) {
40483             return; // should not happen..
40484         }
40485         var reg = false;
40486         for (var r in this.layout.regions) {
40487             reg = this.layout.getRegion(r);
40488             if (reg.getActivePanel()) {
40489                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40490                 reg.setActivePanel(reg.getActivePanel());
40491                 continue;
40492             }
40493             if (!reg.panels.length) {
40494                 continue;
40495             }
40496             reg.showPanel(reg.getPanel(0));
40497         }
40498         
40499         
40500         
40501         
40502     },
40503     
40504     /**
40505      * Returns the nested BorderLayout for this panel
40506      * @return {Roo.BorderLayout} 
40507      */
40508     getLayout : function(){
40509         return this.layout;
40510     },
40511     
40512      /**
40513      * Adds a xtype elements to the layout of the nested panel
40514      * <pre><code>
40515
40516 panel.addxtype({
40517        xtype : 'ContentPanel',
40518        region: 'west',
40519        items: [ .... ]
40520    }
40521 );
40522
40523 panel.addxtype({
40524         xtype : 'NestedLayoutPanel',
40525         region: 'west',
40526         layout: {
40527            center: { },
40528            west: { }   
40529         },
40530         items : [ ... list of content panels or nested layout panels.. ]
40531    }
40532 );
40533 </code></pre>
40534      * @param {Object} cfg Xtype definition of item to add.
40535      */
40536     addxtype : function(cfg) {
40537         return this.layout.addxtype(cfg);
40538     
40539     }
40540 });/*
40541  * Based on:
40542  * Ext JS Library 1.1.1
40543  * Copyright(c) 2006-2007, Ext JS, LLC.
40544  *
40545  * Originally Released Under LGPL - original licence link has changed is not relivant.
40546  *
40547  * Fork - LGPL
40548  * <script type="text/javascript">
40549  */
40550 /**
40551  * @class Roo.TabPanel
40552  * @extends Roo.util.Observable
40553  * A lightweight tab container.
40554  * <br><br>
40555  * Usage:
40556  * <pre><code>
40557 // basic tabs 1, built from existing content
40558 var tabs = new Roo.TabPanel("tabs1");
40559 tabs.addTab("script", "View Script");
40560 tabs.addTab("markup", "View Markup");
40561 tabs.activate("script");
40562
40563 // more advanced tabs, built from javascript
40564 var jtabs = new Roo.TabPanel("jtabs");
40565 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40566
40567 // set up the UpdateManager
40568 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40569 var updater = tab2.getUpdateManager();
40570 updater.setDefaultUrl("ajax1.htm");
40571 tab2.on('activate', updater.refresh, updater, true);
40572
40573 // Use setUrl for Ajax loading
40574 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40575 tab3.setUrl("ajax2.htm", null, true);
40576
40577 // Disabled tab
40578 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40579 tab4.disable();
40580
40581 jtabs.activate("jtabs-1");
40582  * </code></pre>
40583  * @constructor
40584  * Create a new TabPanel.
40585  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40586  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40587  */
40588 Roo.bootstrap.panel.Tabs = function(config){
40589     /**
40590     * The container element for this TabPanel.
40591     * @type Roo.Element
40592     */
40593     this.el = Roo.get(config.el);
40594     delete config.el;
40595     if(config){
40596         if(typeof config == "boolean"){
40597             this.tabPosition = config ? "bottom" : "top";
40598         }else{
40599             Roo.apply(this, config);
40600         }
40601     }
40602     
40603     if(this.tabPosition == "bottom"){
40604         // if tabs are at the bottom = create the body first.
40605         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40606         this.el.addClass("roo-tabs-bottom");
40607     }
40608     // next create the tabs holders
40609     
40610     if (this.tabPosition == "west"){
40611         
40612         var reg = this.region; // fake it..
40613         while (reg) {
40614             if (!reg.mgr.parent) {
40615                 break;
40616             }
40617             reg = reg.mgr.parent.region;
40618         }
40619         Roo.log("got nest?");
40620         Roo.log(reg);
40621         if (reg.mgr.getRegion('west')) {
40622             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40623             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40624             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40625             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40626             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40627         
40628             
40629         }
40630         
40631         
40632     } else {
40633      
40634         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40635         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40636         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40637         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40638     }
40639     
40640     
40641     if(Roo.isIE){
40642         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40643     }
40644     
40645     // finally - if tabs are at the top, then create the body last..
40646     if(this.tabPosition != "bottom"){
40647         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40648          * @type Roo.Element
40649          */
40650         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40651         this.el.addClass("roo-tabs-top");
40652     }
40653     this.items = [];
40654
40655     this.bodyEl.setStyle("position", "relative");
40656
40657     this.active = null;
40658     this.activateDelegate = this.activate.createDelegate(this);
40659
40660     this.addEvents({
40661         /**
40662          * @event tabchange
40663          * Fires when the active tab changes
40664          * @param {Roo.TabPanel} this
40665          * @param {Roo.TabPanelItem} activePanel The new active tab
40666          */
40667         "tabchange": true,
40668         /**
40669          * @event beforetabchange
40670          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40671          * @param {Roo.TabPanel} this
40672          * @param {Object} e Set cancel to true on this object to cancel the tab change
40673          * @param {Roo.TabPanelItem} tab The tab being changed to
40674          */
40675         "beforetabchange" : true
40676     });
40677
40678     Roo.EventManager.onWindowResize(this.onResize, this);
40679     this.cpad = this.el.getPadding("lr");
40680     this.hiddenCount = 0;
40681
40682
40683     // toolbar on the tabbar support...
40684     if (this.toolbar) {
40685         alert("no toolbar support yet");
40686         this.toolbar  = false;
40687         /*
40688         var tcfg = this.toolbar;
40689         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40690         this.toolbar = new Roo.Toolbar(tcfg);
40691         if (Roo.isSafari) {
40692             var tbl = tcfg.container.child('table', true);
40693             tbl.setAttribute('width', '100%');
40694         }
40695         */
40696         
40697     }
40698    
40699
40700
40701     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40702 };
40703
40704 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40705     /*
40706      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40707      */
40708     tabPosition : "top",
40709     /*
40710      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40711      */
40712     currentTabWidth : 0,
40713     /*
40714      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40715      */
40716     minTabWidth : 40,
40717     /*
40718      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40719      */
40720     maxTabWidth : 250,
40721     /*
40722      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40723      */
40724     preferredTabWidth : 175,
40725     /*
40726      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40727      */
40728     resizeTabs : false,
40729     /*
40730      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40731      */
40732     monitorResize : true,
40733     /*
40734      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40735      */
40736     toolbar : false,  // set by caller..
40737     
40738     region : false, /// set by caller
40739     
40740     disableTooltips : true, // not used yet...
40741
40742     /**
40743      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40744      * @param {String} id The id of the div to use <b>or create</b>
40745      * @param {String} text The text for the tab
40746      * @param {String} content (optional) Content to put in the TabPanelItem body
40747      * @param {Boolean} closable (optional) True to create a close icon on the tab
40748      * @return {Roo.TabPanelItem} The created TabPanelItem
40749      */
40750     addTab : function(id, text, content, closable, tpl)
40751     {
40752         var item = new Roo.bootstrap.panel.TabItem({
40753             panel: this,
40754             id : id,
40755             text : text,
40756             closable : closable,
40757             tpl : tpl
40758         });
40759         this.addTabItem(item);
40760         if(content){
40761             item.setContent(content);
40762         }
40763         return item;
40764     },
40765
40766     /**
40767      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40768      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40769      * @return {Roo.TabPanelItem}
40770      */
40771     getTab : function(id){
40772         return this.items[id];
40773     },
40774
40775     /**
40776      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40777      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40778      */
40779     hideTab : function(id){
40780         var t = this.items[id];
40781         if(!t.isHidden()){
40782            t.setHidden(true);
40783            this.hiddenCount++;
40784            this.autoSizeTabs();
40785         }
40786     },
40787
40788     /**
40789      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40790      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40791      */
40792     unhideTab : function(id){
40793         var t = this.items[id];
40794         if(t.isHidden()){
40795            t.setHidden(false);
40796            this.hiddenCount--;
40797            this.autoSizeTabs();
40798         }
40799     },
40800
40801     /**
40802      * Adds an existing {@link Roo.TabPanelItem}.
40803      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40804      */
40805     addTabItem : function(item)
40806     {
40807         this.items[item.id] = item;
40808         this.items.push(item);
40809         this.autoSizeTabs();
40810       //  if(this.resizeTabs){
40811     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40812   //         this.autoSizeTabs();
40813 //        }else{
40814 //            item.autoSize();
40815        // }
40816     },
40817
40818     /**
40819      * Removes a {@link Roo.TabPanelItem}.
40820      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40821      */
40822     removeTab : function(id){
40823         var items = this.items;
40824         var tab = items[id];
40825         if(!tab) { return; }
40826         var index = items.indexOf(tab);
40827         if(this.active == tab && items.length > 1){
40828             var newTab = this.getNextAvailable(index);
40829             if(newTab) {
40830                 newTab.activate();
40831             }
40832         }
40833         this.stripEl.dom.removeChild(tab.pnode.dom);
40834         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40835             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40836         }
40837         items.splice(index, 1);
40838         delete this.items[tab.id];
40839         tab.fireEvent("close", tab);
40840         tab.purgeListeners();
40841         this.autoSizeTabs();
40842     },
40843
40844     getNextAvailable : function(start){
40845         var items = this.items;
40846         var index = start;
40847         // look for a next tab that will slide over to
40848         // replace the one being removed
40849         while(index < items.length){
40850             var item = items[++index];
40851             if(item && !item.isHidden()){
40852                 return item;
40853             }
40854         }
40855         // if one isn't found select the previous tab (on the left)
40856         index = start;
40857         while(index >= 0){
40858             var item = items[--index];
40859             if(item && !item.isHidden()){
40860                 return item;
40861             }
40862         }
40863         return null;
40864     },
40865
40866     /**
40867      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40868      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40869      */
40870     disableTab : function(id){
40871         var tab = this.items[id];
40872         if(tab && this.active != tab){
40873             tab.disable();
40874         }
40875     },
40876
40877     /**
40878      * Enables a {@link Roo.TabPanelItem} that is disabled.
40879      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40880      */
40881     enableTab : function(id){
40882         var tab = this.items[id];
40883         tab.enable();
40884     },
40885
40886     /**
40887      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40888      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40889      * @return {Roo.TabPanelItem} The TabPanelItem.
40890      */
40891     activate : function(id)
40892     {
40893         //Roo.log('activite:'  + id);
40894         
40895         var tab = this.items[id];
40896         if(!tab){
40897             return null;
40898         }
40899         if(tab == this.active || tab.disabled){
40900             return tab;
40901         }
40902         var e = {};
40903         this.fireEvent("beforetabchange", this, e, tab);
40904         if(e.cancel !== true && !tab.disabled){
40905             if(this.active){
40906                 this.active.hide();
40907             }
40908             this.active = this.items[id];
40909             this.active.show();
40910             this.fireEvent("tabchange", this, this.active);
40911         }
40912         return tab;
40913     },
40914
40915     /**
40916      * Gets the active {@link Roo.TabPanelItem}.
40917      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40918      */
40919     getActiveTab : function(){
40920         return this.active;
40921     },
40922
40923     /**
40924      * Updates the tab body element to fit the height of the container element
40925      * for overflow scrolling
40926      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40927      */
40928     syncHeight : function(targetHeight){
40929         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40930         var bm = this.bodyEl.getMargins();
40931         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40932         this.bodyEl.setHeight(newHeight);
40933         return newHeight;
40934     },
40935
40936     onResize : function(){
40937         if(this.monitorResize){
40938             this.autoSizeTabs();
40939         }
40940     },
40941
40942     /**
40943      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40944      */
40945     beginUpdate : function(){
40946         this.updating = true;
40947     },
40948
40949     /**
40950      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40951      */
40952     endUpdate : function(){
40953         this.updating = false;
40954         this.autoSizeTabs();
40955     },
40956
40957     /**
40958      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40959      */
40960     autoSizeTabs : function()
40961     {
40962         var count = this.items.length;
40963         var vcount = count - this.hiddenCount;
40964         
40965         if (vcount < 2) {
40966             this.stripEl.hide();
40967         } else {
40968             this.stripEl.show();
40969         }
40970         
40971         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40972             return;
40973         }
40974         
40975         
40976         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40977         var availWidth = Math.floor(w / vcount);
40978         var b = this.stripBody;
40979         if(b.getWidth() > w){
40980             var tabs = this.items;
40981             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40982             if(availWidth < this.minTabWidth){
40983                 /*if(!this.sleft){    // incomplete scrolling code
40984                     this.createScrollButtons();
40985                 }
40986                 this.showScroll();
40987                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40988             }
40989         }else{
40990             if(this.currentTabWidth < this.preferredTabWidth){
40991                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40992             }
40993         }
40994     },
40995
40996     /**
40997      * Returns the number of tabs in this TabPanel.
40998      * @return {Number}
40999      */
41000      getCount : function(){
41001          return this.items.length;
41002      },
41003
41004     /**
41005      * Resizes all the tabs to the passed width
41006      * @param {Number} The new width
41007      */
41008     setTabWidth : function(width){
41009         this.currentTabWidth = width;
41010         for(var i = 0, len = this.items.length; i < len; i++) {
41011                 if(!this.items[i].isHidden()) {
41012                 this.items[i].setWidth(width);
41013             }
41014         }
41015     },
41016
41017     /**
41018      * Destroys this TabPanel
41019      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41020      */
41021     destroy : function(removeEl){
41022         Roo.EventManager.removeResizeListener(this.onResize, this);
41023         for(var i = 0, len = this.items.length; i < len; i++){
41024             this.items[i].purgeListeners();
41025         }
41026         if(removeEl === true){
41027             this.el.update("");
41028             this.el.remove();
41029         }
41030     },
41031     
41032     createStrip : function(container)
41033     {
41034         var strip = document.createElement("nav");
41035         strip.className = Roo.bootstrap.version == 4 ?
41036             "navbar-light bg-light" : 
41037             "navbar navbar-default"; //"x-tabs-wrap";
41038         container.appendChild(strip);
41039         return strip;
41040     },
41041     
41042     createStripList : function(strip)
41043     {
41044         // div wrapper for retard IE
41045         // returns the "tr" element.
41046         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41047         //'<div class="x-tabs-strip-wrap">'+
41048           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41049           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41050         return strip.firstChild; //.firstChild.firstChild.firstChild;
41051     },
41052     createBody : function(container)
41053     {
41054         var body = document.createElement("div");
41055         Roo.id(body, "tab-body");
41056         //Roo.fly(body).addClass("x-tabs-body");
41057         Roo.fly(body).addClass("tab-content");
41058         container.appendChild(body);
41059         return body;
41060     },
41061     createItemBody :function(bodyEl, id){
41062         var body = Roo.getDom(id);
41063         if(!body){
41064             body = document.createElement("div");
41065             body.id = id;
41066         }
41067         //Roo.fly(body).addClass("x-tabs-item-body");
41068         Roo.fly(body).addClass("tab-pane");
41069          bodyEl.insertBefore(body, bodyEl.firstChild);
41070         return body;
41071     },
41072     /** @private */
41073     createStripElements :  function(stripEl, text, closable, tpl)
41074     {
41075         var td = document.createElement("li"); // was td..
41076         td.className = 'nav-item';
41077         
41078         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41079         
41080         
41081         stripEl.appendChild(td);
41082         /*if(closable){
41083             td.className = "x-tabs-closable";
41084             if(!this.closeTpl){
41085                 this.closeTpl = new Roo.Template(
41086                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41087                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41088                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41089                 );
41090             }
41091             var el = this.closeTpl.overwrite(td, {"text": text});
41092             var close = el.getElementsByTagName("div")[0];
41093             var inner = el.getElementsByTagName("em")[0];
41094             return {"el": el, "close": close, "inner": inner};
41095         } else {
41096         */
41097         // not sure what this is..
41098 //            if(!this.tabTpl){
41099                 //this.tabTpl = new Roo.Template(
41100                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41101                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41102                 //);
41103 //                this.tabTpl = new Roo.Template(
41104 //                   '<a href="#">' +
41105 //                   '<span unselectable="on"' +
41106 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41107 //                            ' >{text}</span></a>'
41108 //                );
41109 //                
41110 //            }
41111
41112
41113             var template = tpl || this.tabTpl || false;
41114             
41115             if(!template){
41116                 template =  new Roo.Template(
41117                         Roo.bootstrap.version == 4 ? 
41118                             (
41119                                 '<a class="nav-link" href="#" unselectable="on"' +
41120                                      (this.disableTooltips ? '' : ' title="{text}"') +
41121                                      ' >{text}</a>'
41122                             ) : (
41123                                 '<a class="nav-link" href="#">' +
41124                                 '<span unselectable="on"' +
41125                                          (this.disableTooltips ? '' : ' title="{text}"') +
41126                                     ' >{text}</span></a>'
41127                             )
41128                 );
41129             }
41130             
41131             switch (typeof(template)) {
41132                 case 'object' :
41133                     break;
41134                 case 'string' :
41135                     template = new Roo.Template(template);
41136                     break;
41137                 default :
41138                     break;
41139             }
41140             
41141             var el = template.overwrite(td, {"text": text});
41142             
41143             var inner = el.getElementsByTagName("span")[0];
41144             
41145             return {"el": el, "inner": inner};
41146             
41147     }
41148         
41149     
41150 });
41151
41152 /**
41153  * @class Roo.TabPanelItem
41154  * @extends Roo.util.Observable
41155  * Represents an individual item (tab plus body) in a TabPanel.
41156  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41157  * @param {String} id The id of this TabPanelItem
41158  * @param {String} text The text for the tab of this TabPanelItem
41159  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41160  */
41161 Roo.bootstrap.panel.TabItem = function(config){
41162     /**
41163      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41164      * @type Roo.TabPanel
41165      */
41166     this.tabPanel = config.panel;
41167     /**
41168      * The id for this TabPanelItem
41169      * @type String
41170      */
41171     this.id = config.id;
41172     /** @private */
41173     this.disabled = false;
41174     /** @private */
41175     this.text = config.text;
41176     /** @private */
41177     this.loaded = false;
41178     this.closable = config.closable;
41179
41180     /**
41181      * The body element for this TabPanelItem.
41182      * @type Roo.Element
41183      */
41184     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41185     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41186     this.bodyEl.setStyle("display", "block");
41187     this.bodyEl.setStyle("zoom", "1");
41188     //this.hideAction();
41189
41190     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41191     /** @private */
41192     this.el = Roo.get(els.el);
41193     this.inner = Roo.get(els.inner, true);
41194      this.textEl = Roo.bootstrap.version == 4 ?
41195         this.el : Roo.get(this.el.dom.firstChild, true);
41196
41197     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41198     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41199
41200     
41201 //    this.el.on("mousedown", this.onTabMouseDown, this);
41202     this.el.on("click", this.onTabClick, this);
41203     /** @private */
41204     if(config.closable){
41205         var c = Roo.get(els.close, true);
41206         c.dom.title = this.closeText;
41207         c.addClassOnOver("close-over");
41208         c.on("click", this.closeClick, this);
41209      }
41210
41211     this.addEvents({
41212          /**
41213          * @event activate
41214          * Fires when this tab becomes the active tab.
41215          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41216          * @param {Roo.TabPanelItem} this
41217          */
41218         "activate": true,
41219         /**
41220          * @event beforeclose
41221          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41222          * @param {Roo.TabPanelItem} this
41223          * @param {Object} e Set cancel to true on this object to cancel the close.
41224          */
41225         "beforeclose": true,
41226         /**
41227          * @event close
41228          * Fires when this tab is closed.
41229          * @param {Roo.TabPanelItem} this
41230          */
41231          "close": true,
41232         /**
41233          * @event deactivate
41234          * Fires when this tab is no longer the active tab.
41235          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41236          * @param {Roo.TabPanelItem} this
41237          */
41238          "deactivate" : true
41239     });
41240     this.hidden = false;
41241
41242     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41243 };
41244
41245 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41246            {
41247     purgeListeners : function(){
41248        Roo.util.Observable.prototype.purgeListeners.call(this);
41249        this.el.removeAllListeners();
41250     },
41251     /**
41252      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41253      */
41254     show : function(){
41255         this.status_node.addClass("active");
41256         this.showAction();
41257         if(Roo.isOpera){
41258             this.tabPanel.stripWrap.repaint();
41259         }
41260         this.fireEvent("activate", this.tabPanel, this);
41261     },
41262
41263     /**
41264      * Returns true if this tab is the active tab.
41265      * @return {Boolean}
41266      */
41267     isActive : function(){
41268         return this.tabPanel.getActiveTab() == this;
41269     },
41270
41271     /**
41272      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41273      */
41274     hide : function(){
41275         this.status_node.removeClass("active");
41276         this.hideAction();
41277         this.fireEvent("deactivate", this.tabPanel, this);
41278     },
41279
41280     hideAction : function(){
41281         this.bodyEl.hide();
41282         this.bodyEl.setStyle("position", "absolute");
41283         this.bodyEl.setLeft("-20000px");
41284         this.bodyEl.setTop("-20000px");
41285     },
41286
41287     showAction : function(){
41288         this.bodyEl.setStyle("position", "relative");
41289         this.bodyEl.setTop("");
41290         this.bodyEl.setLeft("");
41291         this.bodyEl.show();
41292     },
41293
41294     /**
41295      * Set the tooltip for the tab.
41296      * @param {String} tooltip The tab's tooltip
41297      */
41298     setTooltip : function(text){
41299         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41300             this.textEl.dom.qtip = text;
41301             this.textEl.dom.removeAttribute('title');
41302         }else{
41303             this.textEl.dom.title = text;
41304         }
41305     },
41306
41307     onTabClick : function(e){
41308         e.preventDefault();
41309         this.tabPanel.activate(this.id);
41310     },
41311
41312     onTabMouseDown : function(e){
41313         e.preventDefault();
41314         this.tabPanel.activate(this.id);
41315     },
41316 /*
41317     getWidth : function(){
41318         return this.inner.getWidth();
41319     },
41320
41321     setWidth : function(width){
41322         var iwidth = width - this.linode.getPadding("lr");
41323         this.inner.setWidth(iwidth);
41324         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41325         this.linode.setWidth(width);
41326     },
41327 */
41328     /**
41329      * Show or hide the tab
41330      * @param {Boolean} hidden True to hide or false to show.
41331      */
41332     setHidden : function(hidden){
41333         this.hidden = hidden;
41334         this.linode.setStyle("display", hidden ? "none" : "");
41335     },
41336
41337     /**
41338      * Returns true if this tab is "hidden"
41339      * @return {Boolean}
41340      */
41341     isHidden : function(){
41342         return this.hidden;
41343     },
41344
41345     /**
41346      * Returns the text for this tab
41347      * @return {String}
41348      */
41349     getText : function(){
41350         return this.text;
41351     },
41352     /*
41353     autoSize : function(){
41354         //this.el.beginMeasure();
41355         this.textEl.setWidth(1);
41356         /*
41357          *  #2804 [new] Tabs in Roojs
41358          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41359          */
41360         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41361         //this.el.endMeasure();
41362     //},
41363
41364     /**
41365      * Sets the text for the tab (Note: this also sets the tooltip text)
41366      * @param {String} text The tab's text and tooltip
41367      */
41368     setText : function(text){
41369         this.text = text;
41370         this.textEl.update(text);
41371         this.setTooltip(text);
41372         //if(!this.tabPanel.resizeTabs){
41373         //    this.autoSize();
41374         //}
41375     },
41376     /**
41377      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41378      */
41379     activate : function(){
41380         this.tabPanel.activate(this.id);
41381     },
41382
41383     /**
41384      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41385      */
41386     disable : function(){
41387         if(this.tabPanel.active != this){
41388             this.disabled = true;
41389             this.status_node.addClass("disabled");
41390         }
41391     },
41392
41393     /**
41394      * Enables this TabPanelItem if it was previously disabled.
41395      */
41396     enable : function(){
41397         this.disabled = false;
41398         this.status_node.removeClass("disabled");
41399     },
41400
41401     /**
41402      * Sets the content for this TabPanelItem.
41403      * @param {String} content The content
41404      * @param {Boolean} loadScripts true to look for and load scripts
41405      */
41406     setContent : function(content, loadScripts){
41407         this.bodyEl.update(content, loadScripts);
41408     },
41409
41410     /**
41411      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41412      * @return {Roo.UpdateManager} The UpdateManager
41413      */
41414     getUpdateManager : function(){
41415         return this.bodyEl.getUpdateManager();
41416     },
41417
41418     /**
41419      * Set a URL to be used to load the content for this TabPanelItem.
41420      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41421      * @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)
41422      * @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)
41423      * @return {Roo.UpdateManager} The UpdateManager
41424      */
41425     setUrl : function(url, params, loadOnce){
41426         if(this.refreshDelegate){
41427             this.un('activate', this.refreshDelegate);
41428         }
41429         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41430         this.on("activate", this.refreshDelegate);
41431         return this.bodyEl.getUpdateManager();
41432     },
41433
41434     /** @private */
41435     _handleRefresh : function(url, params, loadOnce){
41436         if(!loadOnce || !this.loaded){
41437             var updater = this.bodyEl.getUpdateManager();
41438             updater.update(url, params, this._setLoaded.createDelegate(this));
41439         }
41440     },
41441
41442     /**
41443      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41444      *   Will fail silently if the setUrl method has not been called.
41445      *   This does not activate the panel, just updates its content.
41446      */
41447     refresh : function(){
41448         if(this.refreshDelegate){
41449            this.loaded = false;
41450            this.refreshDelegate();
41451         }
41452     },
41453
41454     /** @private */
41455     _setLoaded : function(){
41456         this.loaded = true;
41457     },
41458
41459     /** @private */
41460     closeClick : function(e){
41461         var o = {};
41462         e.stopEvent();
41463         this.fireEvent("beforeclose", this, o);
41464         if(o.cancel !== true){
41465             this.tabPanel.removeTab(this.id);
41466         }
41467     },
41468     /**
41469      * The text displayed in the tooltip for the close icon.
41470      * @type String
41471      */
41472     closeText : "Close this tab"
41473 });
41474 /**
41475 *    This script refer to:
41476 *    Title: International Telephone Input
41477 *    Author: Jack O'Connor
41478 *    Code version:  v12.1.12
41479 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41480 **/
41481
41482 Roo.bootstrap.PhoneInputData = function() {
41483     var d = [
41484       [
41485         "Afghanistan (‫افغانستان‬‎)",
41486         "af",
41487         "93"
41488       ],
41489       [
41490         "Albania (Shqipëri)",
41491         "al",
41492         "355"
41493       ],
41494       [
41495         "Algeria (‫الجزائر‬‎)",
41496         "dz",
41497         "213"
41498       ],
41499       [
41500         "American Samoa",
41501         "as",
41502         "1684"
41503       ],
41504       [
41505         "Andorra",
41506         "ad",
41507         "376"
41508       ],
41509       [
41510         "Angola",
41511         "ao",
41512         "244"
41513       ],
41514       [
41515         "Anguilla",
41516         "ai",
41517         "1264"
41518       ],
41519       [
41520         "Antigua and Barbuda",
41521         "ag",
41522         "1268"
41523       ],
41524       [
41525         "Argentina",
41526         "ar",
41527         "54"
41528       ],
41529       [
41530         "Armenia (Հայաստան)",
41531         "am",
41532         "374"
41533       ],
41534       [
41535         "Aruba",
41536         "aw",
41537         "297"
41538       ],
41539       [
41540         "Australia",
41541         "au",
41542         "61",
41543         0
41544       ],
41545       [
41546         "Austria (Österreich)",
41547         "at",
41548         "43"
41549       ],
41550       [
41551         "Azerbaijan (Azərbaycan)",
41552         "az",
41553         "994"
41554       ],
41555       [
41556         "Bahamas",
41557         "bs",
41558         "1242"
41559       ],
41560       [
41561         "Bahrain (‫البحرين‬‎)",
41562         "bh",
41563         "973"
41564       ],
41565       [
41566         "Bangladesh (বাংলাদেশ)",
41567         "bd",
41568         "880"
41569       ],
41570       [
41571         "Barbados",
41572         "bb",
41573         "1246"
41574       ],
41575       [
41576         "Belarus (Беларусь)",
41577         "by",
41578         "375"
41579       ],
41580       [
41581         "Belgium (België)",
41582         "be",
41583         "32"
41584       ],
41585       [
41586         "Belize",
41587         "bz",
41588         "501"
41589       ],
41590       [
41591         "Benin (Bénin)",
41592         "bj",
41593         "229"
41594       ],
41595       [
41596         "Bermuda",
41597         "bm",
41598         "1441"
41599       ],
41600       [
41601         "Bhutan (འབྲུག)",
41602         "bt",
41603         "975"
41604       ],
41605       [
41606         "Bolivia",
41607         "bo",
41608         "591"
41609       ],
41610       [
41611         "Bosnia and Herzegovina (Босна и Херцеговина)",
41612         "ba",
41613         "387"
41614       ],
41615       [
41616         "Botswana",
41617         "bw",
41618         "267"
41619       ],
41620       [
41621         "Brazil (Brasil)",
41622         "br",
41623         "55"
41624       ],
41625       [
41626         "British Indian Ocean Territory",
41627         "io",
41628         "246"
41629       ],
41630       [
41631         "British Virgin Islands",
41632         "vg",
41633         "1284"
41634       ],
41635       [
41636         "Brunei",
41637         "bn",
41638         "673"
41639       ],
41640       [
41641         "Bulgaria (България)",
41642         "bg",
41643         "359"
41644       ],
41645       [
41646         "Burkina Faso",
41647         "bf",
41648         "226"
41649       ],
41650       [
41651         "Burundi (Uburundi)",
41652         "bi",
41653         "257"
41654       ],
41655       [
41656         "Cambodia (កម្ពុជា)",
41657         "kh",
41658         "855"
41659       ],
41660       [
41661         "Cameroon (Cameroun)",
41662         "cm",
41663         "237"
41664       ],
41665       [
41666         "Canada",
41667         "ca",
41668         "1",
41669         1,
41670         ["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"]
41671       ],
41672       [
41673         "Cape Verde (Kabu Verdi)",
41674         "cv",
41675         "238"
41676       ],
41677       [
41678         "Caribbean Netherlands",
41679         "bq",
41680         "599",
41681         1
41682       ],
41683       [
41684         "Cayman Islands",
41685         "ky",
41686         "1345"
41687       ],
41688       [
41689         "Central African Republic (République centrafricaine)",
41690         "cf",
41691         "236"
41692       ],
41693       [
41694         "Chad (Tchad)",
41695         "td",
41696         "235"
41697       ],
41698       [
41699         "Chile",
41700         "cl",
41701         "56"
41702       ],
41703       [
41704         "China (中国)",
41705         "cn",
41706         "86"
41707       ],
41708       [
41709         "Christmas Island",
41710         "cx",
41711         "61",
41712         2
41713       ],
41714       [
41715         "Cocos (Keeling) Islands",
41716         "cc",
41717         "61",
41718         1
41719       ],
41720       [
41721         "Colombia",
41722         "co",
41723         "57"
41724       ],
41725       [
41726         "Comoros (‫جزر القمر‬‎)",
41727         "km",
41728         "269"
41729       ],
41730       [
41731         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41732         "cd",
41733         "243"
41734       ],
41735       [
41736         "Congo (Republic) (Congo-Brazzaville)",
41737         "cg",
41738         "242"
41739       ],
41740       [
41741         "Cook Islands",
41742         "ck",
41743         "682"
41744       ],
41745       [
41746         "Costa Rica",
41747         "cr",
41748         "506"
41749       ],
41750       [
41751         "Côte d’Ivoire",
41752         "ci",
41753         "225"
41754       ],
41755       [
41756         "Croatia (Hrvatska)",
41757         "hr",
41758         "385"
41759       ],
41760       [
41761         "Cuba",
41762         "cu",
41763         "53"
41764       ],
41765       [
41766         "Curaçao",
41767         "cw",
41768         "599",
41769         0
41770       ],
41771       [
41772         "Cyprus (Κύπρος)",
41773         "cy",
41774         "357"
41775       ],
41776       [
41777         "Czech Republic (Česká republika)",
41778         "cz",
41779         "420"
41780       ],
41781       [
41782         "Denmark (Danmark)",
41783         "dk",
41784         "45"
41785       ],
41786       [
41787         "Djibouti",
41788         "dj",
41789         "253"
41790       ],
41791       [
41792         "Dominica",
41793         "dm",
41794         "1767"
41795       ],
41796       [
41797         "Dominican Republic (República Dominicana)",
41798         "do",
41799         "1",
41800         2,
41801         ["809", "829", "849"]
41802       ],
41803       [
41804         "Ecuador",
41805         "ec",
41806         "593"
41807       ],
41808       [
41809         "Egypt (‫مصر‬‎)",
41810         "eg",
41811         "20"
41812       ],
41813       [
41814         "El Salvador",
41815         "sv",
41816         "503"
41817       ],
41818       [
41819         "Equatorial Guinea (Guinea Ecuatorial)",
41820         "gq",
41821         "240"
41822       ],
41823       [
41824         "Eritrea",
41825         "er",
41826         "291"
41827       ],
41828       [
41829         "Estonia (Eesti)",
41830         "ee",
41831         "372"
41832       ],
41833       [
41834         "Ethiopia",
41835         "et",
41836         "251"
41837       ],
41838       [
41839         "Falkland Islands (Islas Malvinas)",
41840         "fk",
41841         "500"
41842       ],
41843       [
41844         "Faroe Islands (Føroyar)",
41845         "fo",
41846         "298"
41847       ],
41848       [
41849         "Fiji",
41850         "fj",
41851         "679"
41852       ],
41853       [
41854         "Finland (Suomi)",
41855         "fi",
41856         "358",
41857         0
41858       ],
41859       [
41860         "France",
41861         "fr",
41862         "33"
41863       ],
41864       [
41865         "French Guiana (Guyane française)",
41866         "gf",
41867         "594"
41868       ],
41869       [
41870         "French Polynesia (Polynésie française)",
41871         "pf",
41872         "689"
41873       ],
41874       [
41875         "Gabon",
41876         "ga",
41877         "241"
41878       ],
41879       [
41880         "Gambia",
41881         "gm",
41882         "220"
41883       ],
41884       [
41885         "Georgia (საქართველო)",
41886         "ge",
41887         "995"
41888       ],
41889       [
41890         "Germany (Deutschland)",
41891         "de",
41892         "49"
41893       ],
41894       [
41895         "Ghana (Gaana)",
41896         "gh",
41897         "233"
41898       ],
41899       [
41900         "Gibraltar",
41901         "gi",
41902         "350"
41903       ],
41904       [
41905         "Greece (Ελλάδα)",
41906         "gr",
41907         "30"
41908       ],
41909       [
41910         "Greenland (Kalaallit Nunaat)",
41911         "gl",
41912         "299"
41913       ],
41914       [
41915         "Grenada",
41916         "gd",
41917         "1473"
41918       ],
41919       [
41920         "Guadeloupe",
41921         "gp",
41922         "590",
41923         0
41924       ],
41925       [
41926         "Guam",
41927         "gu",
41928         "1671"
41929       ],
41930       [
41931         "Guatemala",
41932         "gt",
41933         "502"
41934       ],
41935       [
41936         "Guernsey",
41937         "gg",
41938         "44",
41939         1
41940       ],
41941       [
41942         "Guinea (Guinée)",
41943         "gn",
41944         "224"
41945       ],
41946       [
41947         "Guinea-Bissau (Guiné Bissau)",
41948         "gw",
41949         "245"
41950       ],
41951       [
41952         "Guyana",
41953         "gy",
41954         "592"
41955       ],
41956       [
41957         "Haiti",
41958         "ht",
41959         "509"
41960       ],
41961       [
41962         "Honduras",
41963         "hn",
41964         "504"
41965       ],
41966       [
41967         "Hong Kong (香港)",
41968         "hk",
41969         "852"
41970       ],
41971       [
41972         "Hungary (Magyarország)",
41973         "hu",
41974         "36"
41975       ],
41976       [
41977         "Iceland (Ísland)",
41978         "is",
41979         "354"
41980       ],
41981       [
41982         "India (भारत)",
41983         "in",
41984         "91"
41985       ],
41986       [
41987         "Indonesia",
41988         "id",
41989         "62"
41990       ],
41991       [
41992         "Iran (‫ایران‬‎)",
41993         "ir",
41994         "98"
41995       ],
41996       [
41997         "Iraq (‫العراق‬‎)",
41998         "iq",
41999         "964"
42000       ],
42001       [
42002         "Ireland",
42003         "ie",
42004         "353"
42005       ],
42006       [
42007         "Isle of Man",
42008         "im",
42009         "44",
42010         2
42011       ],
42012       [
42013         "Israel (‫ישראל‬‎)",
42014         "il",
42015         "972"
42016       ],
42017       [
42018         "Italy (Italia)",
42019         "it",
42020         "39",
42021         0
42022       ],
42023       [
42024         "Jamaica",
42025         "jm",
42026         "1876"
42027       ],
42028       [
42029         "Japan (日本)",
42030         "jp",
42031         "81"
42032       ],
42033       [
42034         "Jersey",
42035         "je",
42036         "44",
42037         3
42038       ],
42039       [
42040         "Jordan (‫الأردن‬‎)",
42041         "jo",
42042         "962"
42043       ],
42044       [
42045         "Kazakhstan (Казахстан)",
42046         "kz",
42047         "7",
42048         1
42049       ],
42050       [
42051         "Kenya",
42052         "ke",
42053         "254"
42054       ],
42055       [
42056         "Kiribati",
42057         "ki",
42058         "686"
42059       ],
42060       [
42061         "Kosovo",
42062         "xk",
42063         "383"
42064       ],
42065       [
42066         "Kuwait (‫الكويت‬‎)",
42067         "kw",
42068         "965"
42069       ],
42070       [
42071         "Kyrgyzstan (Кыргызстан)",
42072         "kg",
42073         "996"
42074       ],
42075       [
42076         "Laos (ລາວ)",
42077         "la",
42078         "856"
42079       ],
42080       [
42081         "Latvia (Latvija)",
42082         "lv",
42083         "371"
42084       ],
42085       [
42086         "Lebanon (‫لبنان‬‎)",
42087         "lb",
42088         "961"
42089       ],
42090       [
42091         "Lesotho",
42092         "ls",
42093         "266"
42094       ],
42095       [
42096         "Liberia",
42097         "lr",
42098         "231"
42099       ],
42100       [
42101         "Libya (‫ليبيا‬‎)",
42102         "ly",
42103         "218"
42104       ],
42105       [
42106         "Liechtenstein",
42107         "li",
42108         "423"
42109       ],
42110       [
42111         "Lithuania (Lietuva)",
42112         "lt",
42113         "370"
42114       ],
42115       [
42116         "Luxembourg",
42117         "lu",
42118         "352"
42119       ],
42120       [
42121         "Macau (澳門)",
42122         "mo",
42123         "853"
42124       ],
42125       [
42126         "Macedonia (FYROM) (Македонија)",
42127         "mk",
42128         "389"
42129       ],
42130       [
42131         "Madagascar (Madagasikara)",
42132         "mg",
42133         "261"
42134       ],
42135       [
42136         "Malawi",
42137         "mw",
42138         "265"
42139       ],
42140       [
42141         "Malaysia",
42142         "my",
42143         "60"
42144       ],
42145       [
42146         "Maldives",
42147         "mv",
42148         "960"
42149       ],
42150       [
42151         "Mali",
42152         "ml",
42153         "223"
42154       ],
42155       [
42156         "Malta",
42157         "mt",
42158         "356"
42159       ],
42160       [
42161         "Marshall Islands",
42162         "mh",
42163         "692"
42164       ],
42165       [
42166         "Martinique",
42167         "mq",
42168         "596"
42169       ],
42170       [
42171         "Mauritania (‫موريتانيا‬‎)",
42172         "mr",
42173         "222"
42174       ],
42175       [
42176         "Mauritius (Moris)",
42177         "mu",
42178         "230"
42179       ],
42180       [
42181         "Mayotte",
42182         "yt",
42183         "262",
42184         1
42185       ],
42186       [
42187         "Mexico (México)",
42188         "mx",
42189         "52"
42190       ],
42191       [
42192         "Micronesia",
42193         "fm",
42194         "691"
42195       ],
42196       [
42197         "Moldova (Republica Moldova)",
42198         "md",
42199         "373"
42200       ],
42201       [
42202         "Monaco",
42203         "mc",
42204         "377"
42205       ],
42206       [
42207         "Mongolia (Монгол)",
42208         "mn",
42209         "976"
42210       ],
42211       [
42212         "Montenegro (Crna Gora)",
42213         "me",
42214         "382"
42215       ],
42216       [
42217         "Montserrat",
42218         "ms",
42219         "1664"
42220       ],
42221       [
42222         "Morocco (‫المغرب‬‎)",
42223         "ma",
42224         "212",
42225         0
42226       ],
42227       [
42228         "Mozambique (Moçambique)",
42229         "mz",
42230         "258"
42231       ],
42232       [
42233         "Myanmar (Burma) (မြန်မာ)",
42234         "mm",
42235         "95"
42236       ],
42237       [
42238         "Namibia (Namibië)",
42239         "na",
42240         "264"
42241       ],
42242       [
42243         "Nauru",
42244         "nr",
42245         "674"
42246       ],
42247       [
42248         "Nepal (नेपाल)",
42249         "np",
42250         "977"
42251       ],
42252       [
42253         "Netherlands (Nederland)",
42254         "nl",
42255         "31"
42256       ],
42257       [
42258         "New Caledonia (Nouvelle-Calédonie)",
42259         "nc",
42260         "687"
42261       ],
42262       [
42263         "New Zealand",
42264         "nz",
42265         "64"
42266       ],
42267       [
42268         "Nicaragua",
42269         "ni",
42270         "505"
42271       ],
42272       [
42273         "Niger (Nijar)",
42274         "ne",
42275         "227"
42276       ],
42277       [
42278         "Nigeria",
42279         "ng",
42280         "234"
42281       ],
42282       [
42283         "Niue",
42284         "nu",
42285         "683"
42286       ],
42287       [
42288         "Norfolk Island",
42289         "nf",
42290         "672"
42291       ],
42292       [
42293         "North Korea (조선 민주주의 인민 공화국)",
42294         "kp",
42295         "850"
42296       ],
42297       [
42298         "Northern Mariana Islands",
42299         "mp",
42300         "1670"
42301       ],
42302       [
42303         "Norway (Norge)",
42304         "no",
42305         "47",
42306         0
42307       ],
42308       [
42309         "Oman (‫عُمان‬‎)",
42310         "om",
42311         "968"
42312       ],
42313       [
42314         "Pakistan (‫پاکستان‬‎)",
42315         "pk",
42316         "92"
42317       ],
42318       [
42319         "Palau",
42320         "pw",
42321         "680"
42322       ],
42323       [
42324         "Palestine (‫فلسطين‬‎)",
42325         "ps",
42326         "970"
42327       ],
42328       [
42329         "Panama (Panamá)",
42330         "pa",
42331         "507"
42332       ],
42333       [
42334         "Papua New Guinea",
42335         "pg",
42336         "675"
42337       ],
42338       [
42339         "Paraguay",
42340         "py",
42341         "595"
42342       ],
42343       [
42344         "Peru (Perú)",
42345         "pe",
42346         "51"
42347       ],
42348       [
42349         "Philippines",
42350         "ph",
42351         "63"
42352       ],
42353       [
42354         "Poland (Polska)",
42355         "pl",
42356         "48"
42357       ],
42358       [
42359         "Portugal",
42360         "pt",
42361         "351"
42362       ],
42363       [
42364         "Puerto Rico",
42365         "pr",
42366         "1",
42367         3,
42368         ["787", "939"]
42369       ],
42370       [
42371         "Qatar (‫قطر‬‎)",
42372         "qa",
42373         "974"
42374       ],
42375       [
42376         "Réunion (La Réunion)",
42377         "re",
42378         "262",
42379         0
42380       ],
42381       [
42382         "Romania (România)",
42383         "ro",
42384         "40"
42385       ],
42386       [
42387         "Russia (Россия)",
42388         "ru",
42389         "7",
42390         0
42391       ],
42392       [
42393         "Rwanda",
42394         "rw",
42395         "250"
42396       ],
42397       [
42398         "Saint Barthélemy",
42399         "bl",
42400         "590",
42401         1
42402       ],
42403       [
42404         "Saint Helena",
42405         "sh",
42406         "290"
42407       ],
42408       [
42409         "Saint Kitts and Nevis",
42410         "kn",
42411         "1869"
42412       ],
42413       [
42414         "Saint Lucia",
42415         "lc",
42416         "1758"
42417       ],
42418       [
42419         "Saint Martin (Saint-Martin (partie française))",
42420         "mf",
42421         "590",
42422         2
42423       ],
42424       [
42425         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42426         "pm",
42427         "508"
42428       ],
42429       [
42430         "Saint Vincent and the Grenadines",
42431         "vc",
42432         "1784"
42433       ],
42434       [
42435         "Samoa",
42436         "ws",
42437         "685"
42438       ],
42439       [
42440         "San Marino",
42441         "sm",
42442         "378"
42443       ],
42444       [
42445         "São Tomé and Príncipe (São Tomé e Príncipe)",
42446         "st",
42447         "239"
42448       ],
42449       [
42450         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42451         "sa",
42452         "966"
42453       ],
42454       [
42455         "Senegal (Sénégal)",
42456         "sn",
42457         "221"
42458       ],
42459       [
42460         "Serbia (Србија)",
42461         "rs",
42462         "381"
42463       ],
42464       [
42465         "Seychelles",
42466         "sc",
42467         "248"
42468       ],
42469       [
42470         "Sierra Leone",
42471         "sl",
42472         "232"
42473       ],
42474       [
42475         "Singapore",
42476         "sg",
42477         "65"
42478       ],
42479       [
42480         "Sint Maarten",
42481         "sx",
42482         "1721"
42483       ],
42484       [
42485         "Slovakia (Slovensko)",
42486         "sk",
42487         "421"
42488       ],
42489       [
42490         "Slovenia (Slovenija)",
42491         "si",
42492         "386"
42493       ],
42494       [
42495         "Solomon Islands",
42496         "sb",
42497         "677"
42498       ],
42499       [
42500         "Somalia (Soomaaliya)",
42501         "so",
42502         "252"
42503       ],
42504       [
42505         "South Africa",
42506         "za",
42507         "27"
42508       ],
42509       [
42510         "South Korea (대한민국)",
42511         "kr",
42512         "82"
42513       ],
42514       [
42515         "South Sudan (‫جنوب السودان‬‎)",
42516         "ss",
42517         "211"
42518       ],
42519       [
42520         "Spain (España)",
42521         "es",
42522         "34"
42523       ],
42524       [
42525         "Sri Lanka (ශ්‍රී ලංකාව)",
42526         "lk",
42527         "94"
42528       ],
42529       [
42530         "Sudan (‫السودان‬‎)",
42531         "sd",
42532         "249"
42533       ],
42534       [
42535         "Suriname",
42536         "sr",
42537         "597"
42538       ],
42539       [
42540         "Svalbard and Jan Mayen",
42541         "sj",
42542         "47",
42543         1
42544       ],
42545       [
42546         "Swaziland",
42547         "sz",
42548         "268"
42549       ],
42550       [
42551         "Sweden (Sverige)",
42552         "se",
42553         "46"
42554       ],
42555       [
42556         "Switzerland (Schweiz)",
42557         "ch",
42558         "41"
42559       ],
42560       [
42561         "Syria (‫سوريا‬‎)",
42562         "sy",
42563         "963"
42564       ],
42565       [
42566         "Taiwan (台灣)",
42567         "tw",
42568         "886"
42569       ],
42570       [
42571         "Tajikistan",
42572         "tj",
42573         "992"
42574       ],
42575       [
42576         "Tanzania",
42577         "tz",
42578         "255"
42579       ],
42580       [
42581         "Thailand (ไทย)",
42582         "th",
42583         "66"
42584       ],
42585       [
42586         "Timor-Leste",
42587         "tl",
42588         "670"
42589       ],
42590       [
42591         "Togo",
42592         "tg",
42593         "228"
42594       ],
42595       [
42596         "Tokelau",
42597         "tk",
42598         "690"
42599       ],
42600       [
42601         "Tonga",
42602         "to",
42603         "676"
42604       ],
42605       [
42606         "Trinidad and Tobago",
42607         "tt",
42608         "1868"
42609       ],
42610       [
42611         "Tunisia (‫تونس‬‎)",
42612         "tn",
42613         "216"
42614       ],
42615       [
42616         "Turkey (Türkiye)",
42617         "tr",
42618         "90"
42619       ],
42620       [
42621         "Turkmenistan",
42622         "tm",
42623         "993"
42624       ],
42625       [
42626         "Turks and Caicos Islands",
42627         "tc",
42628         "1649"
42629       ],
42630       [
42631         "Tuvalu",
42632         "tv",
42633         "688"
42634       ],
42635       [
42636         "U.S. Virgin Islands",
42637         "vi",
42638         "1340"
42639       ],
42640       [
42641         "Uganda",
42642         "ug",
42643         "256"
42644       ],
42645       [
42646         "Ukraine (Україна)",
42647         "ua",
42648         "380"
42649       ],
42650       [
42651         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42652         "ae",
42653         "971"
42654       ],
42655       [
42656         "United Kingdom",
42657         "gb",
42658         "44",
42659         0
42660       ],
42661       [
42662         "United States",
42663         "us",
42664         "1",
42665         0
42666       ],
42667       [
42668         "Uruguay",
42669         "uy",
42670         "598"
42671       ],
42672       [
42673         "Uzbekistan (Oʻzbekiston)",
42674         "uz",
42675         "998"
42676       ],
42677       [
42678         "Vanuatu",
42679         "vu",
42680         "678"
42681       ],
42682       [
42683         "Vatican City (Città del Vaticano)",
42684         "va",
42685         "39",
42686         1
42687       ],
42688       [
42689         "Venezuela",
42690         "ve",
42691         "58"
42692       ],
42693       [
42694         "Vietnam (Việt Nam)",
42695         "vn",
42696         "84"
42697       ],
42698       [
42699         "Wallis and Futuna (Wallis-et-Futuna)",
42700         "wf",
42701         "681"
42702       ],
42703       [
42704         "Western Sahara (‫الصحراء الغربية‬‎)",
42705         "eh",
42706         "212",
42707         1
42708       ],
42709       [
42710         "Yemen (‫اليمن‬‎)",
42711         "ye",
42712         "967"
42713       ],
42714       [
42715         "Zambia",
42716         "zm",
42717         "260"
42718       ],
42719       [
42720         "Zimbabwe",
42721         "zw",
42722         "263"
42723       ],
42724       [
42725         "Åland Islands",
42726         "ax",
42727         "358",
42728         1
42729       ]
42730   ];
42731   
42732   return d;
42733 }/**
42734 *    This script refer to:
42735 *    Title: International Telephone Input
42736 *    Author: Jack O'Connor
42737 *    Code version:  v12.1.12
42738 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42739 **/
42740
42741 /**
42742  * @class Roo.bootstrap.PhoneInput
42743  * @extends Roo.bootstrap.TriggerField
42744  * An input with International dial-code selection
42745  
42746  * @cfg {String} defaultDialCode default '+852'
42747  * @cfg {Array} preferedCountries default []
42748   
42749  * @constructor
42750  * Create a new PhoneInput.
42751  * @param {Object} config Configuration options
42752  */
42753
42754 Roo.bootstrap.PhoneInput = function(config) {
42755     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42756 };
42757
42758 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42759         
42760         listWidth: undefined,
42761         
42762         selectedClass: 'active',
42763         
42764         invalidClass : "has-warning",
42765         
42766         validClass: 'has-success',
42767         
42768         allowed: '0123456789',
42769         
42770         max_length: 15,
42771         
42772         /**
42773          * @cfg {String} defaultDialCode The default dial code when initializing the input
42774          */
42775         defaultDialCode: '+852',
42776         
42777         /**
42778          * @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
42779          */
42780         preferedCountries: false,
42781         
42782         getAutoCreate : function()
42783         {
42784             var data = Roo.bootstrap.PhoneInputData();
42785             var align = this.labelAlign || this.parentLabelAlign();
42786             var id = Roo.id();
42787             
42788             this.allCountries = [];
42789             this.dialCodeMapping = [];
42790             
42791             for (var i = 0; i < data.length; i++) {
42792               var c = data[i];
42793               this.allCountries[i] = {
42794                 name: c[0],
42795                 iso2: c[1],
42796                 dialCode: c[2],
42797                 priority: c[3] || 0,
42798                 areaCodes: c[4] || null
42799               };
42800               this.dialCodeMapping[c[2]] = {
42801                   name: c[0],
42802                   iso2: c[1],
42803                   priority: c[3] || 0,
42804                   areaCodes: c[4] || null
42805               };
42806             }
42807             
42808             var cfg = {
42809                 cls: 'form-group',
42810                 cn: []
42811             };
42812             
42813             var input =  {
42814                 tag: 'input',
42815                 id : id,
42816                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42817                 maxlength: this.max_length,
42818                 cls : 'form-control tel-input',
42819                 autocomplete: 'new-password'
42820             };
42821             
42822             var hiddenInput = {
42823                 tag: 'input',
42824                 type: 'hidden',
42825                 cls: 'hidden-tel-input'
42826             };
42827             
42828             if (this.name) {
42829                 hiddenInput.name = this.name;
42830             }
42831             
42832             if (this.disabled) {
42833                 input.disabled = true;
42834             }
42835             
42836             var flag_container = {
42837                 tag: 'div',
42838                 cls: 'flag-box',
42839                 cn: [
42840                     {
42841                         tag: 'div',
42842                         cls: 'flag'
42843                     },
42844                     {
42845                         tag: 'div',
42846                         cls: 'caret'
42847                     }
42848                 ]
42849             };
42850             
42851             var box = {
42852                 tag: 'div',
42853                 cls: this.hasFeedback ? 'has-feedback' : '',
42854                 cn: [
42855                     hiddenInput,
42856                     input,
42857                     {
42858                         tag: 'input',
42859                         cls: 'dial-code-holder',
42860                         disabled: true
42861                     }
42862                 ]
42863             };
42864             
42865             var container = {
42866                 cls: 'roo-select2-container input-group',
42867                 cn: [
42868                     flag_container,
42869                     box
42870                 ]
42871             };
42872             
42873             if (this.fieldLabel.length) {
42874                 var indicator = {
42875                     tag: 'i',
42876                     tooltip: 'This field is required'
42877                 };
42878                 
42879                 var label = {
42880                     tag: 'label',
42881                     'for':  id,
42882                     cls: 'control-label',
42883                     cn: []
42884                 };
42885                 
42886                 var label_text = {
42887                     tag: 'span',
42888                     html: this.fieldLabel
42889                 };
42890                 
42891                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42892                 label.cn = [
42893                     indicator,
42894                     label_text
42895                 ];
42896                 
42897                 if(this.indicatorpos == 'right') {
42898                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42899                     label.cn = [
42900                         label_text,
42901                         indicator
42902                     ];
42903                 }
42904                 
42905                 if(align == 'left') {
42906                     container = {
42907                         tag: 'div',
42908                         cn: [
42909                             container
42910                         ]
42911                     };
42912                     
42913                     if(this.labelWidth > 12){
42914                         label.style = "width: " + this.labelWidth + 'px';
42915                     }
42916                     if(this.labelWidth < 13 && this.labelmd == 0){
42917                         this.labelmd = this.labelWidth;
42918                     }
42919                     if(this.labellg > 0){
42920                         label.cls += ' col-lg-' + this.labellg;
42921                         input.cls += ' col-lg-' + (12 - this.labellg);
42922                     }
42923                     if(this.labelmd > 0){
42924                         label.cls += ' col-md-' + this.labelmd;
42925                         container.cls += ' col-md-' + (12 - this.labelmd);
42926                     }
42927                     if(this.labelsm > 0){
42928                         label.cls += ' col-sm-' + this.labelsm;
42929                         container.cls += ' col-sm-' + (12 - this.labelsm);
42930                     }
42931                     if(this.labelxs > 0){
42932                         label.cls += ' col-xs-' + this.labelxs;
42933                         container.cls += ' col-xs-' + (12 - this.labelxs);
42934                     }
42935                 }
42936             }
42937             
42938             cfg.cn = [
42939                 label,
42940                 container
42941             ];
42942             
42943             var settings = this;
42944             
42945             ['xs','sm','md','lg'].map(function(size){
42946                 if (settings[size]) {
42947                     cfg.cls += ' col-' + size + '-' + settings[size];
42948                 }
42949             });
42950             
42951             this.store = new Roo.data.Store({
42952                 proxy : new Roo.data.MemoryProxy({}),
42953                 reader : new Roo.data.JsonReader({
42954                     fields : [
42955                         {
42956                             'name' : 'name',
42957                             'type' : 'string'
42958                         },
42959                         {
42960                             'name' : 'iso2',
42961                             'type' : 'string'
42962                         },
42963                         {
42964                             'name' : 'dialCode',
42965                             'type' : 'string'
42966                         },
42967                         {
42968                             'name' : 'priority',
42969                             'type' : 'string'
42970                         },
42971                         {
42972                             'name' : 'areaCodes',
42973                             'type' : 'string'
42974                         }
42975                     ]
42976                 })
42977             });
42978             
42979             if(!this.preferedCountries) {
42980                 this.preferedCountries = [
42981                     'hk',
42982                     'gb',
42983                     'us'
42984                 ];
42985             }
42986             
42987             var p = this.preferedCountries.reverse();
42988             
42989             if(p) {
42990                 for (var i = 0; i < p.length; i++) {
42991                     for (var j = 0; j < this.allCountries.length; j++) {
42992                         if(this.allCountries[j].iso2 == p[i]) {
42993                             var t = this.allCountries[j];
42994                             this.allCountries.splice(j,1);
42995                             this.allCountries.unshift(t);
42996                         }
42997                     } 
42998                 }
42999             }
43000             
43001             this.store.proxy.data = {
43002                 success: true,
43003                 data: this.allCountries
43004             };
43005             
43006             return cfg;
43007         },
43008         
43009         initEvents : function()
43010         {
43011             this.createList();
43012             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43013             
43014             this.indicator = this.indicatorEl();
43015             this.flag = this.flagEl();
43016             this.dialCodeHolder = this.dialCodeHolderEl();
43017             
43018             this.trigger = this.el.select('div.flag-box',true).first();
43019             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43020             
43021             var _this = this;
43022             
43023             (function(){
43024                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43025                 _this.list.setWidth(lw);
43026             }).defer(100);
43027             
43028             this.list.on('mouseover', this.onViewOver, this);
43029             this.list.on('mousemove', this.onViewMove, this);
43030             this.inputEl().on("keyup", this.onKeyUp, this);
43031             this.inputEl().on("keypress", this.onKeyPress, this);
43032             
43033             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43034
43035             this.view = new Roo.View(this.list, this.tpl, {
43036                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43037             });
43038             
43039             this.view.on('click', this.onViewClick, this);
43040             this.setValue(this.defaultDialCode);
43041         },
43042         
43043         onTriggerClick : function(e)
43044         {
43045             Roo.log('trigger click');
43046             if(this.disabled){
43047                 return;
43048             }
43049             
43050             if(this.isExpanded()){
43051                 this.collapse();
43052                 this.hasFocus = false;
43053             }else {
43054                 this.store.load({});
43055                 this.hasFocus = true;
43056                 this.expand();
43057             }
43058         },
43059         
43060         isExpanded : function()
43061         {
43062             return this.list.isVisible();
43063         },
43064         
43065         collapse : function()
43066         {
43067             if(!this.isExpanded()){
43068                 return;
43069             }
43070             this.list.hide();
43071             Roo.get(document).un('mousedown', this.collapseIf, this);
43072             Roo.get(document).un('mousewheel', this.collapseIf, this);
43073             this.fireEvent('collapse', this);
43074             this.validate();
43075         },
43076         
43077         expand : function()
43078         {
43079             Roo.log('expand');
43080
43081             if(this.isExpanded() || !this.hasFocus){
43082                 return;
43083             }
43084             
43085             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43086             this.list.setWidth(lw);
43087             
43088             this.list.show();
43089             this.restrictHeight();
43090             
43091             Roo.get(document).on('mousedown', this.collapseIf, this);
43092             Roo.get(document).on('mousewheel', this.collapseIf, this);
43093             
43094             this.fireEvent('expand', this);
43095         },
43096         
43097         restrictHeight : function()
43098         {
43099             this.list.alignTo(this.inputEl(), this.listAlign);
43100             this.list.alignTo(this.inputEl(), this.listAlign);
43101         },
43102         
43103         onViewOver : function(e, t)
43104         {
43105             if(this.inKeyMode){
43106                 return;
43107             }
43108             var item = this.view.findItemFromChild(t);
43109             
43110             if(item){
43111                 var index = this.view.indexOf(item);
43112                 this.select(index, false);
43113             }
43114         },
43115
43116         // private
43117         onViewClick : function(view, doFocus, el, e)
43118         {
43119             var index = this.view.getSelectedIndexes()[0];
43120             
43121             var r = this.store.getAt(index);
43122             
43123             if(r){
43124                 this.onSelect(r, index);
43125             }
43126             if(doFocus !== false && !this.blockFocus){
43127                 this.inputEl().focus();
43128             }
43129         },
43130         
43131         onViewMove : function(e, t)
43132         {
43133             this.inKeyMode = false;
43134         },
43135         
43136         select : function(index, scrollIntoView)
43137         {
43138             this.selectedIndex = index;
43139             this.view.select(index);
43140             if(scrollIntoView !== false){
43141                 var el = this.view.getNode(index);
43142                 if(el){
43143                     this.list.scrollChildIntoView(el, false);
43144                 }
43145             }
43146         },
43147         
43148         createList : function()
43149         {
43150             this.list = Roo.get(document.body).createChild({
43151                 tag: 'ul',
43152                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43153                 style: 'display:none'
43154             });
43155             
43156             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43157         },
43158         
43159         collapseIf : function(e)
43160         {
43161             var in_combo  = e.within(this.el);
43162             var in_list =  e.within(this.list);
43163             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43164             
43165             if (in_combo || in_list || is_list) {
43166                 return;
43167             }
43168             this.collapse();
43169         },
43170         
43171         onSelect : function(record, index)
43172         {
43173             if(this.fireEvent('beforeselect', this, record, index) !== false){
43174                 
43175                 this.setFlagClass(record.data.iso2);
43176                 this.setDialCode(record.data.dialCode);
43177                 this.hasFocus = false;
43178                 this.collapse();
43179                 this.fireEvent('select', this, record, index);
43180             }
43181         },
43182         
43183         flagEl : function()
43184         {
43185             var flag = this.el.select('div.flag',true).first();
43186             if(!flag){
43187                 return false;
43188             }
43189             return flag;
43190         },
43191         
43192         dialCodeHolderEl : function()
43193         {
43194             var d = this.el.select('input.dial-code-holder',true).first();
43195             if(!d){
43196                 return false;
43197             }
43198             return d;
43199         },
43200         
43201         setDialCode : function(v)
43202         {
43203             this.dialCodeHolder.dom.value = '+'+v;
43204         },
43205         
43206         setFlagClass : function(n)
43207         {
43208             this.flag.dom.className = 'flag '+n;
43209         },
43210         
43211         getValue : function()
43212         {
43213             var v = this.inputEl().getValue();
43214             if(this.dialCodeHolder) {
43215                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43216             }
43217             return v;
43218         },
43219         
43220         setValue : function(v)
43221         {
43222             var d = this.getDialCode(v);
43223             
43224             //invalid dial code
43225             if(v.length == 0 || !d || d.length == 0) {
43226                 if(this.rendered){
43227                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43228                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43229                 }
43230                 return;
43231             }
43232             
43233             //valid dial code
43234             this.setFlagClass(this.dialCodeMapping[d].iso2);
43235             this.setDialCode(d);
43236             this.inputEl().dom.value = v.replace('+'+d,'');
43237             this.hiddenEl().dom.value = this.getValue();
43238             
43239             this.validate();
43240         },
43241         
43242         getDialCode : function(v)
43243         {
43244             v = v ||  '';
43245             
43246             if (v.length == 0) {
43247                 return this.dialCodeHolder.dom.value;
43248             }
43249             
43250             var dialCode = "";
43251             if (v.charAt(0) != "+") {
43252                 return false;
43253             }
43254             var numericChars = "";
43255             for (var i = 1; i < v.length; i++) {
43256               var c = v.charAt(i);
43257               if (!isNaN(c)) {
43258                 numericChars += c;
43259                 if (this.dialCodeMapping[numericChars]) {
43260                   dialCode = v.substr(1, i);
43261                 }
43262                 if (numericChars.length == 4) {
43263                   break;
43264                 }
43265               }
43266             }
43267             return dialCode;
43268         },
43269         
43270         reset : function()
43271         {
43272             this.setValue(this.defaultDialCode);
43273             this.validate();
43274         },
43275         
43276         hiddenEl : function()
43277         {
43278             return this.el.select('input.hidden-tel-input',true).first();
43279         },
43280         
43281         // after setting val
43282         onKeyUp : function(e){
43283             this.setValue(this.getValue());
43284         },
43285         
43286         onKeyPress : function(e){
43287             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43288                 e.stopEvent();
43289             }
43290         }
43291         
43292 });
43293 /**
43294  * @class Roo.bootstrap.MoneyField
43295  * @extends Roo.bootstrap.ComboBox
43296  * Bootstrap MoneyField class
43297  * 
43298  * @constructor
43299  * Create a new MoneyField.
43300  * @param {Object} config Configuration options
43301  */
43302
43303 Roo.bootstrap.MoneyField = function(config) {
43304     
43305     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43306     
43307 };
43308
43309 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43310     
43311     /**
43312      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43313      */
43314     allowDecimals : true,
43315     /**
43316      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43317      */
43318     decimalSeparator : ".",
43319     /**
43320      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43321      */
43322     decimalPrecision : 0,
43323     /**
43324      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43325      */
43326     allowNegative : true,
43327     /**
43328      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43329      */
43330     allowZero: true,
43331     /**
43332      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43333      */
43334     minValue : Number.NEGATIVE_INFINITY,
43335     /**
43336      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43337      */
43338     maxValue : Number.MAX_VALUE,
43339     /**
43340      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43341      */
43342     minText : "The minimum value for this field is {0}",
43343     /**
43344      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43345      */
43346     maxText : "The maximum value for this field is {0}",
43347     /**
43348      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43349      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43350      */
43351     nanText : "{0} is not a valid number",
43352     /**
43353      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43354      */
43355     castInt : true,
43356     /**
43357      * @cfg {String} defaults currency of the MoneyField
43358      * value should be in lkey
43359      */
43360     defaultCurrency : false,
43361     /**
43362      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43363      */
43364     thousandsDelimiter : false,
43365     /**
43366      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43367      */
43368     max_length: false,
43369     
43370     inputlg : 9,
43371     inputmd : 9,
43372     inputsm : 9,
43373     inputxs : 6,
43374     
43375     store : false,
43376     
43377     getAutoCreate : function()
43378     {
43379         var align = this.labelAlign || this.parentLabelAlign();
43380         
43381         var id = Roo.id();
43382
43383         var cfg = {
43384             cls: 'form-group',
43385             cn: []
43386         };
43387
43388         var input =  {
43389             tag: 'input',
43390             id : id,
43391             cls : 'form-control roo-money-amount-input',
43392             autocomplete: 'new-password'
43393         };
43394         
43395         var hiddenInput = {
43396             tag: 'input',
43397             type: 'hidden',
43398             id: Roo.id(),
43399             cls: 'hidden-number-input'
43400         };
43401         
43402         if(this.max_length) {
43403             input.maxlength = this.max_length; 
43404         }
43405         
43406         if (this.name) {
43407             hiddenInput.name = this.name;
43408         }
43409
43410         if (this.disabled) {
43411             input.disabled = true;
43412         }
43413
43414         var clg = 12 - this.inputlg;
43415         var cmd = 12 - this.inputmd;
43416         var csm = 12 - this.inputsm;
43417         var cxs = 12 - this.inputxs;
43418         
43419         var container = {
43420             tag : 'div',
43421             cls : 'row roo-money-field',
43422             cn : [
43423                 {
43424                     tag : 'div',
43425                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43426                     cn : [
43427                         {
43428                             tag : 'div',
43429                             cls: 'roo-select2-container input-group',
43430                             cn: [
43431                                 {
43432                                     tag : 'input',
43433                                     cls : 'form-control roo-money-currency-input',
43434                                     autocomplete: 'new-password',
43435                                     readOnly : 1,
43436                                     name : this.currencyName
43437                                 },
43438                                 {
43439                                     tag :'span',
43440                                     cls : 'input-group-addon',
43441                                     cn : [
43442                                         {
43443                                             tag: 'span',
43444                                             cls: 'caret'
43445                                         }
43446                                     ]
43447                                 }
43448                             ]
43449                         }
43450                     ]
43451                 },
43452                 {
43453                     tag : 'div',
43454                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43455                     cn : [
43456                         {
43457                             tag: 'div',
43458                             cls: this.hasFeedback ? 'has-feedback' : '',
43459                             cn: [
43460                                 input
43461                             ]
43462                         }
43463                     ]
43464                 }
43465             ]
43466             
43467         };
43468         
43469         if (this.fieldLabel.length) {
43470             var indicator = {
43471                 tag: 'i',
43472                 tooltip: 'This field is required'
43473             };
43474
43475             var label = {
43476                 tag: 'label',
43477                 'for':  id,
43478                 cls: 'control-label',
43479                 cn: []
43480             };
43481
43482             var label_text = {
43483                 tag: 'span',
43484                 html: this.fieldLabel
43485             };
43486
43487             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43488             label.cn = [
43489                 indicator,
43490                 label_text
43491             ];
43492
43493             if(this.indicatorpos == 'right') {
43494                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43495                 label.cn = [
43496                     label_text,
43497                     indicator
43498                 ];
43499             }
43500
43501             if(align == 'left') {
43502                 container = {
43503                     tag: 'div',
43504                     cn: [
43505                         container
43506                     ]
43507                 };
43508
43509                 if(this.labelWidth > 12){
43510                     label.style = "width: " + this.labelWidth + 'px';
43511                 }
43512                 if(this.labelWidth < 13 && this.labelmd == 0){
43513                     this.labelmd = this.labelWidth;
43514                 }
43515                 if(this.labellg > 0){
43516                     label.cls += ' col-lg-' + this.labellg;
43517                     input.cls += ' col-lg-' + (12 - this.labellg);
43518                 }
43519                 if(this.labelmd > 0){
43520                     label.cls += ' col-md-' + this.labelmd;
43521                     container.cls += ' col-md-' + (12 - this.labelmd);
43522                 }
43523                 if(this.labelsm > 0){
43524                     label.cls += ' col-sm-' + this.labelsm;
43525                     container.cls += ' col-sm-' + (12 - this.labelsm);
43526                 }
43527                 if(this.labelxs > 0){
43528                     label.cls += ' col-xs-' + this.labelxs;
43529                     container.cls += ' col-xs-' + (12 - this.labelxs);
43530                 }
43531             }
43532         }
43533
43534         cfg.cn = [
43535             label,
43536             container,
43537             hiddenInput
43538         ];
43539         
43540         var settings = this;
43541
43542         ['xs','sm','md','lg'].map(function(size){
43543             if (settings[size]) {
43544                 cfg.cls += ' col-' + size + '-' + settings[size];
43545             }
43546         });
43547         
43548         return cfg;
43549     },
43550     
43551     initEvents : function()
43552     {
43553         this.indicator = this.indicatorEl();
43554         
43555         this.initCurrencyEvent();
43556         
43557         this.initNumberEvent();
43558     },
43559     
43560     initCurrencyEvent : function()
43561     {
43562         if (!this.store) {
43563             throw "can not find store for combo";
43564         }
43565         
43566         this.store = Roo.factory(this.store, Roo.data);
43567         this.store.parent = this;
43568         
43569         this.createList();
43570         
43571         this.triggerEl = this.el.select('.input-group-addon', true).first();
43572         
43573         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43574         
43575         var _this = this;
43576         
43577         (function(){
43578             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43579             _this.list.setWidth(lw);
43580         }).defer(100);
43581         
43582         this.list.on('mouseover', this.onViewOver, this);
43583         this.list.on('mousemove', this.onViewMove, this);
43584         this.list.on('scroll', this.onViewScroll, this);
43585         
43586         if(!this.tpl){
43587             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43588         }
43589         
43590         this.view = new Roo.View(this.list, this.tpl, {
43591             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43592         });
43593         
43594         this.view.on('click', this.onViewClick, this);
43595         
43596         this.store.on('beforeload', this.onBeforeLoad, this);
43597         this.store.on('load', this.onLoad, this);
43598         this.store.on('loadexception', this.onLoadException, this);
43599         
43600         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43601             "up" : function(e){
43602                 this.inKeyMode = true;
43603                 this.selectPrev();
43604             },
43605
43606             "down" : function(e){
43607                 if(!this.isExpanded()){
43608                     this.onTriggerClick();
43609                 }else{
43610                     this.inKeyMode = true;
43611                     this.selectNext();
43612                 }
43613             },
43614
43615             "enter" : function(e){
43616                 this.collapse();
43617                 
43618                 if(this.fireEvent("specialkey", this, e)){
43619                     this.onViewClick(false);
43620                 }
43621                 
43622                 return true;
43623             },
43624
43625             "esc" : function(e){
43626                 this.collapse();
43627             },
43628
43629             "tab" : function(e){
43630                 this.collapse();
43631                 
43632                 if(this.fireEvent("specialkey", this, e)){
43633                     this.onViewClick(false);
43634                 }
43635                 
43636                 return true;
43637             },
43638
43639             scope : this,
43640
43641             doRelay : function(foo, bar, hname){
43642                 if(hname == 'down' || this.scope.isExpanded()){
43643                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43644                 }
43645                 return true;
43646             },
43647
43648             forceKeyDown: true
43649         });
43650         
43651         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43652         
43653     },
43654     
43655     initNumberEvent : function(e)
43656     {
43657         this.inputEl().on("keydown" , this.fireKey,  this);
43658         this.inputEl().on("focus", this.onFocus,  this);
43659         this.inputEl().on("blur", this.onBlur,  this);
43660         
43661         this.inputEl().relayEvent('keyup', this);
43662         
43663         if(this.indicator){
43664             this.indicator.addClass('invisible');
43665         }
43666  
43667         this.originalValue = this.getValue();
43668         
43669         if(this.validationEvent == 'keyup'){
43670             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43671             this.inputEl().on('keyup', this.filterValidation, this);
43672         }
43673         else if(this.validationEvent !== false){
43674             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43675         }
43676         
43677         if(this.selectOnFocus){
43678             this.on("focus", this.preFocus, this);
43679             
43680         }
43681         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43682             this.inputEl().on("keypress", this.filterKeys, this);
43683         } else {
43684             this.inputEl().relayEvent('keypress', this);
43685         }
43686         
43687         var allowed = "0123456789";
43688         
43689         if(this.allowDecimals){
43690             allowed += this.decimalSeparator;
43691         }
43692         
43693         if(this.allowNegative){
43694             allowed += "-";
43695         }
43696         
43697         if(this.thousandsDelimiter) {
43698             allowed += ",";
43699         }
43700         
43701         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43702         
43703         var keyPress = function(e){
43704             
43705             var k = e.getKey();
43706             
43707             var c = e.getCharCode();
43708             
43709             if(
43710                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43711                     allowed.indexOf(String.fromCharCode(c)) === -1
43712             ){
43713                 e.stopEvent();
43714                 return;
43715             }
43716             
43717             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43718                 return;
43719             }
43720             
43721             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43722                 e.stopEvent();
43723             }
43724         };
43725         
43726         this.inputEl().on("keypress", keyPress, this);
43727         
43728     },
43729     
43730     onTriggerClick : function(e)
43731     {   
43732         if(this.disabled){
43733             return;
43734         }
43735         
43736         this.page = 0;
43737         this.loadNext = false;
43738         
43739         if(this.isExpanded()){
43740             this.collapse();
43741             return;
43742         }
43743         
43744         this.hasFocus = true;
43745         
43746         if(this.triggerAction == 'all') {
43747             this.doQuery(this.allQuery, true);
43748             return;
43749         }
43750         
43751         this.doQuery(this.getRawValue());
43752     },
43753     
43754     getCurrency : function()
43755     {   
43756         var v = this.currencyEl().getValue();
43757         
43758         return v;
43759     },
43760     
43761     restrictHeight : function()
43762     {
43763         this.list.alignTo(this.currencyEl(), this.listAlign);
43764         this.list.alignTo(this.currencyEl(), this.listAlign);
43765     },
43766     
43767     onViewClick : function(view, doFocus, el, e)
43768     {
43769         var index = this.view.getSelectedIndexes()[0];
43770         
43771         var r = this.store.getAt(index);
43772         
43773         if(r){
43774             this.onSelect(r, index);
43775         }
43776     },
43777     
43778     onSelect : function(record, index){
43779         
43780         if(this.fireEvent('beforeselect', this, record, index) !== false){
43781         
43782             this.setFromCurrencyData(index > -1 ? record.data : false);
43783             
43784             this.collapse();
43785             
43786             this.fireEvent('select', this, record, index);
43787         }
43788     },
43789     
43790     setFromCurrencyData : function(o)
43791     {
43792         var currency = '';
43793         
43794         this.lastCurrency = o;
43795         
43796         if (this.currencyField) {
43797             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43798         } else {
43799             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43800         }
43801         
43802         this.lastSelectionText = currency;
43803         
43804         //setting default currency
43805         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43806             this.setCurrency(this.defaultCurrency);
43807             return;
43808         }
43809         
43810         this.setCurrency(currency);
43811     },
43812     
43813     setFromData : function(o)
43814     {
43815         var c = {};
43816         
43817         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43818         
43819         this.setFromCurrencyData(c);
43820         
43821         var value = '';
43822         
43823         if (this.name) {
43824             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43825         } else {
43826             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43827         }
43828         
43829         this.setValue(value);
43830         
43831     },
43832     
43833     setCurrency : function(v)
43834     {   
43835         this.currencyValue = v;
43836         
43837         if(this.rendered){
43838             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43839             this.validate();
43840         }
43841     },
43842     
43843     setValue : function(v)
43844     {
43845         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43846         
43847         this.value = v;
43848         
43849         if(this.rendered){
43850             
43851             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43852             
43853             this.inputEl().dom.value = (v == '') ? '' :
43854                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43855             
43856             if(!this.allowZero && v === '0') {
43857                 this.hiddenEl().dom.value = '';
43858                 this.inputEl().dom.value = '';
43859             }
43860             
43861             this.validate();
43862         }
43863     },
43864     
43865     getRawValue : function()
43866     {
43867         var v = this.inputEl().getValue();
43868         
43869         return v;
43870     },
43871     
43872     getValue : function()
43873     {
43874         return this.fixPrecision(this.parseValue(this.getRawValue()));
43875     },
43876     
43877     parseValue : function(value)
43878     {
43879         if(this.thousandsDelimiter) {
43880             value += "";
43881             r = new RegExp(",", "g");
43882             value = value.replace(r, "");
43883         }
43884         
43885         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43886         return isNaN(value) ? '' : value;
43887         
43888     },
43889     
43890     fixPrecision : function(value)
43891     {
43892         if(this.thousandsDelimiter) {
43893             value += "";
43894             r = new RegExp(",", "g");
43895             value = value.replace(r, "");
43896         }
43897         
43898         var nan = isNaN(value);
43899         
43900         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43901             return nan ? '' : value;
43902         }
43903         return parseFloat(value).toFixed(this.decimalPrecision);
43904     },
43905     
43906     decimalPrecisionFcn : function(v)
43907     {
43908         return Math.floor(v);
43909     },
43910     
43911     validateValue : function(value)
43912     {
43913         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43914             return false;
43915         }
43916         
43917         var num = this.parseValue(value);
43918         
43919         if(isNaN(num)){
43920             this.markInvalid(String.format(this.nanText, value));
43921             return false;
43922         }
43923         
43924         if(num < this.minValue){
43925             this.markInvalid(String.format(this.minText, this.minValue));
43926             return false;
43927         }
43928         
43929         if(num > this.maxValue){
43930             this.markInvalid(String.format(this.maxText, this.maxValue));
43931             return false;
43932         }
43933         
43934         return true;
43935     },
43936     
43937     validate : function()
43938     {
43939         if(this.disabled || this.allowBlank){
43940             this.markValid();
43941             return true;
43942         }
43943         
43944         var currency = this.getCurrency();
43945         
43946         if(this.validateValue(this.getRawValue()) && currency.length){
43947             this.markValid();
43948             return true;
43949         }
43950         
43951         this.markInvalid();
43952         return false;
43953     },
43954     
43955     getName: function()
43956     {
43957         return this.name;
43958     },
43959     
43960     beforeBlur : function()
43961     {
43962         if(!this.castInt){
43963             return;
43964         }
43965         
43966         var v = this.parseValue(this.getRawValue());
43967         
43968         if(v || v == 0){
43969             this.setValue(v);
43970         }
43971     },
43972     
43973     onBlur : function()
43974     {
43975         this.beforeBlur();
43976         
43977         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43978             //this.el.removeClass(this.focusClass);
43979         }
43980         
43981         this.hasFocus = false;
43982         
43983         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43984             this.validate();
43985         }
43986         
43987         var v = this.getValue();
43988         
43989         if(String(v) !== String(this.startValue)){
43990             this.fireEvent('change', this, v, this.startValue);
43991         }
43992         
43993         this.fireEvent("blur", this);
43994     },
43995     
43996     inputEl : function()
43997     {
43998         return this.el.select('.roo-money-amount-input', true).first();
43999     },
44000     
44001     currencyEl : function()
44002     {
44003         return this.el.select('.roo-money-currency-input', true).first();
44004     },
44005     
44006     hiddenEl : function()
44007     {
44008         return this.el.select('input.hidden-number-input',true).first();
44009     }
44010     
44011 });/**
44012  * @class Roo.bootstrap.BezierSignature
44013  * @extends Roo.bootstrap.Component
44014  * Bootstrap BezierSignature class
44015  * This script refer to:
44016  *    Title: Signature Pad
44017  *    Author: szimek
44018  *    Availability: https://github.com/szimek/signature_pad
44019  *
44020  * @constructor
44021  * Create a new BezierSignature
44022  * @param {Object} config The config object
44023  */
44024
44025 Roo.bootstrap.BezierSignature = function(config){
44026     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44027     this.addEvents({
44028         "resize" : true
44029     });
44030 };
44031
44032 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44033 {
44034      
44035     curve_data: [],
44036     
44037     is_empty: true,
44038     
44039     mouse_btn_down: true,
44040     
44041     /**
44042      * @cfg {int} canvas height
44043      */
44044     canvas_height: '200px',
44045     
44046     /**
44047      * @cfg {float|function} Radius of a single dot.
44048      */ 
44049     dot_size: false,
44050     
44051     /**
44052      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44053      */
44054     min_width: 0.5,
44055     
44056     /**
44057      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44058      */
44059     max_width: 2.5,
44060     
44061     /**
44062      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44063      */
44064     throttle: 16,
44065     
44066     /**
44067      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44068      */
44069     min_distance: 5,
44070     
44071     /**
44072      * @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.
44073      */
44074     bg_color: 'rgba(0, 0, 0, 0)',
44075     
44076     /**
44077      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44078      */
44079     dot_color: 'black',
44080     
44081     /**
44082      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44083      */ 
44084     velocity_filter_weight: 0.7,
44085     
44086     /**
44087      * @cfg {function} Callback when stroke begin. 
44088      */
44089     onBegin: false,
44090     
44091     /**
44092      * @cfg {function} Callback when stroke end.
44093      */
44094     onEnd: false,
44095     
44096     getAutoCreate : function()
44097     {
44098         var cls = 'roo-signature column';
44099         
44100         if(this.cls){
44101             cls += ' ' + this.cls;
44102         }
44103         
44104         var col_sizes = [
44105             'lg',
44106             'md',
44107             'sm',
44108             'xs'
44109         ];
44110         
44111         for(var i = 0; i < col_sizes.length; i++) {
44112             if(this[col_sizes[i]]) {
44113                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44114             }
44115         }
44116         
44117         var cfg = {
44118             tag: 'div',
44119             cls: cls,
44120             cn: [
44121                 {
44122                     tag: 'div',
44123                     cls: 'roo-signature-body',
44124                     cn: [
44125                         {
44126                             tag: 'canvas',
44127                             cls: 'roo-signature-body-canvas',
44128                             height: this.canvas_height,
44129                             width: this.canvas_width
44130                         }
44131                     ]
44132                 },
44133                 {
44134                     tag: 'input',
44135                     type: 'file',
44136                     style: 'display: none'
44137                 }
44138             ]
44139         };
44140         
44141         return cfg;
44142     },
44143     
44144     initEvents: function() 
44145     {
44146         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44147         
44148         var canvas = this.canvasEl();
44149         
44150         // mouse && touch event swapping...
44151         canvas.dom.style.touchAction = 'none';
44152         canvas.dom.style.msTouchAction = 'none';
44153         
44154         this.mouse_btn_down = false;
44155         canvas.on('mousedown', this._handleMouseDown, this);
44156         canvas.on('mousemove', this._handleMouseMove, this);
44157         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44158         
44159         if (window.PointerEvent) {
44160             canvas.on('pointerdown', this._handleMouseDown, this);
44161             canvas.on('pointermove', this._handleMouseMove, this);
44162             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44163         }
44164         
44165         if ('ontouchstart' in window) {
44166             canvas.on('touchstart', this._handleTouchStart, this);
44167             canvas.on('touchmove', this._handleTouchMove, this);
44168             canvas.on('touchend', this._handleTouchEnd, this);
44169         }
44170         
44171         Roo.EventManager.onWindowResize(this.resize, this, true);
44172         
44173         // file input event
44174         this.fileEl().on('change', this.uploadImage, this);
44175         
44176         this.clear();
44177         
44178         this.resize();
44179     },
44180     
44181     resize: function(){
44182         
44183         var canvas = this.canvasEl().dom;
44184         var ctx = this.canvasElCtx();
44185         var img_data = false;
44186         
44187         if(canvas.width > 0) {
44188             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44189         }
44190         // setting canvas width will clean img data
44191         canvas.width = 0;
44192         
44193         var style = window.getComputedStyle ? 
44194             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44195             
44196         var padding_left = parseInt(style.paddingLeft) || 0;
44197         var padding_right = parseInt(style.paddingRight) || 0;
44198         
44199         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44200         
44201         if(img_data) {
44202             ctx.putImageData(img_data, 0, 0);
44203         }
44204     },
44205     
44206     _handleMouseDown: function(e)
44207     {
44208         if (e.browserEvent.which === 1) {
44209             this.mouse_btn_down = true;
44210             this.strokeBegin(e);
44211         }
44212     },
44213     
44214     _handleMouseMove: function (e)
44215     {
44216         if (this.mouse_btn_down) {
44217             this.strokeMoveUpdate(e);
44218         }
44219     },
44220     
44221     _handleMouseUp: function (e)
44222     {
44223         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44224             this.mouse_btn_down = false;
44225             this.strokeEnd(e);
44226         }
44227     },
44228     
44229     _handleTouchStart: function (e) {
44230         
44231         e.preventDefault();
44232         if (e.browserEvent.targetTouches.length === 1) {
44233             // var touch = e.browserEvent.changedTouches[0];
44234             // this.strokeBegin(touch);
44235             
44236              this.strokeBegin(e); // assume e catching the correct xy...
44237         }
44238     },
44239     
44240     _handleTouchMove: function (e) {
44241         e.preventDefault();
44242         // var touch = event.targetTouches[0];
44243         // _this._strokeMoveUpdate(touch);
44244         this.strokeMoveUpdate(e);
44245     },
44246     
44247     _handleTouchEnd: function (e) {
44248         var wasCanvasTouched = e.target === this.canvasEl().dom;
44249         if (wasCanvasTouched) {
44250             e.preventDefault();
44251             // var touch = event.changedTouches[0];
44252             // _this._strokeEnd(touch);
44253             this.strokeEnd(e);
44254         }
44255     },
44256     
44257     reset: function () {
44258         this._lastPoints = [];
44259         this._lastVelocity = 0;
44260         this._lastWidth = (this.min_width + this.max_width) / 2;
44261         this.canvasElCtx().fillStyle = this.dot_color;
44262     },
44263     
44264     strokeMoveUpdate: function(e)
44265     {
44266         this.strokeUpdate(e);
44267         
44268         if (this.throttle) {
44269             this.throttleStroke(this.strokeUpdate, this.throttle);
44270         }
44271         else {
44272             this.strokeUpdate(e);
44273         }
44274     },
44275     
44276     strokeBegin: function(e)
44277     {
44278         var newPointGroup = {
44279             color: this.dot_color,
44280             points: []
44281         };
44282         
44283         if (typeof this.onBegin === 'function') {
44284             this.onBegin(e);
44285         }
44286         
44287         this.curve_data.push(newPointGroup);
44288         this.reset();
44289         this.strokeUpdate(e);
44290     },
44291     
44292     strokeUpdate: function(e)
44293     {
44294         var rect = this.canvasEl().dom.getBoundingClientRect();
44295         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44296         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44297         var lastPoints = lastPointGroup.points;
44298         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44299         var isLastPointTooClose = lastPoint
44300             ? point.distanceTo(lastPoint) <= this.min_distance
44301             : false;
44302         var color = lastPointGroup.color;
44303         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44304             var curve = this.addPoint(point);
44305             if (!lastPoint) {
44306                 this.drawDot({color: color, point: point});
44307             }
44308             else if (curve) {
44309                 this.drawCurve({color: color, curve: curve});
44310             }
44311             lastPoints.push({
44312                 time: point.time,
44313                 x: point.x,
44314                 y: point.y
44315             });
44316         }
44317     },
44318     
44319     strokeEnd: function(e)
44320     {
44321         this.strokeUpdate(e);
44322         if (typeof this.onEnd === 'function') {
44323             this.onEnd(e);
44324         }
44325     },
44326     
44327     addPoint:  function (point) {
44328         var _lastPoints = this._lastPoints;
44329         _lastPoints.push(point);
44330         if (_lastPoints.length > 2) {
44331             if (_lastPoints.length === 3) {
44332                 _lastPoints.unshift(_lastPoints[0]);
44333             }
44334             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44335             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44336             _lastPoints.shift();
44337             return curve;
44338         }
44339         return null;
44340     },
44341     
44342     calculateCurveWidths: function (startPoint, endPoint) {
44343         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44344             (1 - this.velocity_filter_weight) * this._lastVelocity;
44345
44346         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44347         var widths = {
44348             end: newWidth,
44349             start: this._lastWidth
44350         };
44351         
44352         this._lastVelocity = velocity;
44353         this._lastWidth = newWidth;
44354         return widths;
44355     },
44356     
44357     drawDot: function (_a) {
44358         var color = _a.color, point = _a.point;
44359         var ctx = this.canvasElCtx();
44360         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44361         ctx.beginPath();
44362         this.drawCurveSegment(point.x, point.y, width);
44363         ctx.closePath();
44364         ctx.fillStyle = color;
44365         ctx.fill();
44366     },
44367     
44368     drawCurve: function (_a) {
44369         var color = _a.color, curve = _a.curve;
44370         var ctx = this.canvasElCtx();
44371         var widthDelta = curve.endWidth - curve.startWidth;
44372         var drawSteps = Math.floor(curve.length()) * 2;
44373         ctx.beginPath();
44374         ctx.fillStyle = color;
44375         for (var i = 0; i < drawSteps; i += 1) {
44376         var t = i / drawSteps;
44377         var tt = t * t;
44378         var ttt = tt * t;
44379         var u = 1 - t;
44380         var uu = u * u;
44381         var uuu = uu * u;
44382         var x = uuu * curve.startPoint.x;
44383         x += 3 * uu * t * curve.control1.x;
44384         x += 3 * u * tt * curve.control2.x;
44385         x += ttt * curve.endPoint.x;
44386         var y = uuu * curve.startPoint.y;
44387         y += 3 * uu * t * curve.control1.y;
44388         y += 3 * u * tt * curve.control2.y;
44389         y += ttt * curve.endPoint.y;
44390         var width = curve.startWidth + ttt * widthDelta;
44391         this.drawCurveSegment(x, y, width);
44392         }
44393         ctx.closePath();
44394         ctx.fill();
44395     },
44396     
44397     drawCurveSegment: function (x, y, width) {
44398         var ctx = this.canvasElCtx();
44399         ctx.moveTo(x, y);
44400         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44401         this.is_empty = false;
44402     },
44403     
44404     clear: function()
44405     {
44406         var ctx = this.canvasElCtx();
44407         var canvas = this.canvasEl().dom;
44408         ctx.fillStyle = this.bg_color;
44409         ctx.clearRect(0, 0, canvas.width, canvas.height);
44410         ctx.fillRect(0, 0, canvas.width, canvas.height);
44411         this.curve_data = [];
44412         this.reset();
44413         this.is_empty = true;
44414     },
44415     
44416     fileEl: function()
44417     {
44418         return  this.el.select('input',true).first();
44419     },
44420     
44421     canvasEl: function()
44422     {
44423         return this.el.select('canvas',true).first();
44424     },
44425     
44426     canvasElCtx: function()
44427     {
44428         return this.el.select('canvas',true).first().dom.getContext('2d');
44429     },
44430     
44431     getImage: function(type)
44432     {
44433         if(this.is_empty) {
44434             return false;
44435         }
44436         
44437         // encryption ?
44438         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44439     },
44440     
44441     drawFromImage: function(img_src)
44442     {
44443         var img = new Image();
44444         
44445         img.onload = function(){
44446             this.canvasElCtx().drawImage(img, 0, 0);
44447         }.bind(this);
44448         
44449         img.src = img_src;
44450         
44451         this.is_empty = false;
44452     },
44453     
44454     selectImage: function()
44455     {
44456         this.fileEl().dom.click();
44457     },
44458     
44459     uploadImage: function(e)
44460     {
44461         var reader = new FileReader();
44462         
44463         reader.onload = function(e){
44464             var img = new Image();
44465             img.onload = function(){
44466                 this.reset();
44467                 this.canvasElCtx().drawImage(img, 0, 0);
44468             }.bind(this);
44469             img.src = e.target.result;
44470         }.bind(this);
44471         
44472         reader.readAsDataURL(e.target.files[0]);
44473     },
44474     
44475     // Bezier Point Constructor
44476     Point: (function () {
44477         function Point(x, y, time) {
44478             this.x = x;
44479             this.y = y;
44480             this.time = time || Date.now();
44481         }
44482         Point.prototype.distanceTo = function (start) {
44483             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44484         };
44485         Point.prototype.equals = function (other) {
44486             return this.x === other.x && this.y === other.y && this.time === other.time;
44487         };
44488         Point.prototype.velocityFrom = function (start) {
44489             return this.time !== start.time
44490             ? this.distanceTo(start) / (this.time - start.time)
44491             : 0;
44492         };
44493         return Point;
44494     }()),
44495     
44496     
44497     // Bezier Constructor
44498     Bezier: (function () {
44499         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44500             this.startPoint = startPoint;
44501             this.control2 = control2;
44502             this.control1 = control1;
44503             this.endPoint = endPoint;
44504             this.startWidth = startWidth;
44505             this.endWidth = endWidth;
44506         }
44507         Bezier.fromPoints = function (points, widths, scope) {
44508             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44509             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44510             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44511         };
44512         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44513             var dx1 = s1.x - s2.x;
44514             var dy1 = s1.y - s2.y;
44515             var dx2 = s2.x - s3.x;
44516             var dy2 = s2.y - s3.y;
44517             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44518             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44519             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44520             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44521             var dxm = m1.x - m2.x;
44522             var dym = m1.y - m2.y;
44523             var k = l2 / (l1 + l2);
44524             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44525             var tx = s2.x - cm.x;
44526             var ty = s2.y - cm.y;
44527             return {
44528                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44529                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44530             };
44531         };
44532         Bezier.prototype.length = function () {
44533             var steps = 10;
44534             var length = 0;
44535             var px;
44536             var py;
44537             for (var i = 0; i <= steps; i += 1) {
44538                 var t = i / steps;
44539                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44540                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44541                 if (i > 0) {
44542                     var xdiff = cx - px;
44543                     var ydiff = cy - py;
44544                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44545                 }
44546                 px = cx;
44547                 py = cy;
44548             }
44549             return length;
44550         };
44551         Bezier.prototype.point = function (t, start, c1, c2, end) {
44552             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44553             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44554             + (3.0 * c2 * (1.0 - t) * t * t)
44555             + (end * t * t * t);
44556         };
44557         return Bezier;
44558     }()),
44559     
44560     throttleStroke: function(fn, wait) {
44561       if (wait === void 0) { wait = 250; }
44562       var previous = 0;
44563       var timeout = null;
44564       var result;
44565       var storedContext;
44566       var storedArgs;
44567       var later = function () {
44568           previous = Date.now();
44569           timeout = null;
44570           result = fn.apply(storedContext, storedArgs);
44571           if (!timeout) {
44572               storedContext = null;
44573               storedArgs = [];
44574           }
44575       };
44576       return function wrapper() {
44577           var args = [];
44578           for (var _i = 0; _i < arguments.length; _i++) {
44579               args[_i] = arguments[_i];
44580           }
44581           var now = Date.now();
44582           var remaining = wait - (now - previous);
44583           storedContext = this;
44584           storedArgs = args;
44585           if (remaining <= 0 || remaining > wait) {
44586               if (timeout) {
44587                   clearTimeout(timeout);
44588                   timeout = null;
44589               }
44590               previous = now;
44591               result = fn.apply(storedContext, storedArgs);
44592               if (!timeout) {
44593                   storedContext = null;
44594                   storedArgs = [];
44595               }
44596           }
44597           else if (!timeout) {
44598               timeout = window.setTimeout(later, remaining);
44599           }
44600           return result;
44601       };
44602   }
44603   
44604 });
44605
44606  
44607
44608