Roo/bootstrap/Card.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     header_imageEl : false,
2079     
2080     layoutCls : function()
2081     {
2082         var cls = '';
2083         var t = this;
2084         Roo.log(this.margin_bottom.length);
2085         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2086             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2087             
2088             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2089                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2090             }
2091             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2092                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2093             }
2094         });
2095         
2096         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2097             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2098                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2099             }
2100         });
2101         
2102         // more generic support?
2103         if (this.hidden) {
2104             cls += ' d-none';
2105         }
2106         
2107         return cls;
2108     },
2109  
2110        // Roo.log("Call onRender: " + this.xtype);
2111         /*  We are looking at something like this.
2112 <div class="card">
2113     <img src="..." class="card-img-top" alt="...">
2114     <div class="card-body">
2115         <h5 class="card-title">Card title</h5>
2116          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2117
2118         >> this bit is really the body...
2119         <div> << we will ad dthis in hopefully it will not break shit.
2120         
2121         ** card text does not actually have any styling...
2122         
2123             <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>
2124         
2125         </div> <<
2126           <a href="#" class="card-link">Card link</a>
2127           
2128     </div>
2129     <div class="card-footer">
2130         <small class="text-muted">Last updated 3 mins ago</small>
2131     </div>
2132 </div>
2133          */
2134     getAutoCreate : function(){
2135         
2136         var cfg = {
2137             tag : 'div',
2138             cls : 'card',
2139             cn : [ ]
2140         };
2141         
2142         if (this.weight.length && this.weight != 'light') {
2143             cfg.cls += ' text-white';
2144         } else {
2145             cfg.cls += ' text-dark'; // need as it's nested..
2146         }
2147         if (this.weight.length) {
2148             cfg.cls += ' bg-' + this.weight;
2149         }
2150         
2151         cfg.cls += ' ' + this.layoutCls(); 
2152         
2153         var hdr = false;
2154         var hdr_ctr = false;
2155         if (this.header.length) {
2156             hdr = {
2157                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2158                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2159                 cn : []
2160             };
2161             cfg.cn.push(hdr);
2162             hdr_ctr = hdr;
2163         } else {
2164             hdr = {
2165                 tag : 'div',
2166                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2167                 cn : []
2168             };
2169             cfg.cn.push(hdr);
2170             hdr_ctr = hdr;
2171         }
2172         if (this.collapsable) {
2173             hdr_ctr = {
2174             tag : 'a',
2175             cls : 'd-block user-select-none',
2176             cn: [
2177                     {
2178                         tag: 'i',
2179                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2180                     }
2181                    
2182                 ]
2183             };
2184             hdr.cn.push(hdr_ctr);
2185         }
2186         
2187         hdr_ctr.cn.push(        {
2188             tag: 'span',
2189             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2190             html : this.header
2191         });
2192         
2193         
2194         if (this.header_image.length) {
2195             cfg.cn.push({
2196                 tag : 'img',
2197                 cls : 'card-img-top',
2198                 src: this.header_image // escape?
2199             });
2200         } else {
2201             cfg.cn.push({
2202                     tag : 'div',
2203                     cls : 'card-img-top d-none' 
2204                 });
2205         }
2206             
2207         var body = {
2208             tag : 'div',
2209             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2210             cn : []
2211         };
2212         var obody = body;
2213         if (this.collapsable || this.rotateable) {
2214             obody = {
2215                 tag: 'div',
2216                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2217                 cn : [  body ]
2218             };
2219         }
2220         
2221         cfg.cn.push(obody);
2222         
2223         if (this.title.length) {
2224             body.cn.push({
2225                 tag : 'div',
2226                 cls : 'card-title',
2227                 src: this.title // escape?
2228             });
2229         }  
2230         
2231         if (this.subtitle.length) {
2232             body.cn.push({
2233                 tag : 'div',
2234                 cls : 'card-title',
2235                 src: this.subtitle // escape?
2236             });
2237         }
2238         
2239         body.cn.push({
2240             tag : 'div',
2241             cls : 'roo-card-body-ctr'
2242         });
2243         
2244         if (this.html.length) {
2245             body.cn.push({
2246                 tag: 'div',
2247                 html : this.html
2248             });
2249         }
2250         // fixme ? handle objects?
2251         
2252         if (this.footer.length) {
2253            
2254             cfg.cn.push({
2255                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2256                 html : this.footer
2257             });
2258             
2259         } else {
2260             cfg.cn.push({cls : 'card-footer d-none'});
2261         }
2262         
2263         // footer...
2264         
2265         return cfg;
2266     },
2267     
2268     
2269     getCardHeader : function()
2270     {
2271         var  ret = this.el.select('.card-header',true).first();
2272         if (ret.hasClass('d-none')) {
2273             ret.removeClass('d-none');
2274         }
2275         
2276         return ret;
2277     },
2278     getCardFooter : function()
2279     {
2280         var  ret = this.el.select('.card-footer',true).first();
2281         if (ret.hasClass('d-none')) {
2282             ret.removeClass('d-none');
2283         }
2284         
2285         return ret;
2286     },
2287     getCardImageTop : function()
2288     {
2289         var  ret = this.header_imageEl;
2290         if (ret.hasClass('d-none')) {
2291             ret.removeClass('d-none');
2292         }
2293             
2294         return ret;
2295     },
2296     
2297     getChildContainer : function()
2298     {
2299         
2300         if(!this.el){
2301             return false;
2302         }
2303         return this.el.select('.roo-card-body-ctr',true).first();    
2304     },
2305     
2306     initEvents: function() 
2307     {
2308         this.bodyEl = this.el.select('.card-body',true).first(); 
2309         this.containerEl = this.getChildContainer();
2310         if(this.dragable){
2311             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2312                     containerScroll: true,
2313                     ddGroup: this.drag_group || 'default_card_drag_group'
2314             });
2315             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2316         }
2317         if (this.dropable) {
2318             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2319                 containerScroll: true,
2320                 ddGroup: this.drop_group || 'default_card_drag_group'
2321             });
2322             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2323             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2324             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2325             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2326             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2327         }
2328         
2329         if (this.collapsable) {
2330             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2331         }
2332         if (this.rotateable) {
2333             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2334         }
2335         this.collapsableEl = this.el.select('.roo-collapsable').first();
2336          
2337         this.footerEl = this.el.select('.card-footer').first();
2338         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2339         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2340         this.headerEl = this.el.select('.card-header',true).first();
2341         
2342         if (this.rotated) {
2343             this.el.addClass('roo-card-rotated');
2344             this.fireEvent('rotate', this, true);
2345         }
2346         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2347         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2348         
2349     },
2350     getDragData : function(e)
2351     {
2352         var target = this.getEl();
2353         if (target) {
2354             //this.handleSelection(e);
2355             
2356             var dragData = {
2357                 source: this,
2358                 copy: false,
2359                 nodes: this.getEl(),
2360                 records: []
2361             };
2362             
2363             
2364             dragData.ddel = target.dom ;    // the div element
2365             Roo.log(target.getWidth( ));
2366             dragData.ddel.style.width = target.getWidth() + 'px';
2367             
2368             return dragData;
2369         }
2370         return false;
2371     },
2372     /**
2373     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2374     *    whole Element becomes the target, and this causes the drop gesture to append.
2375     */
2376     getTargetFromEvent : function(e, dragged_card_el)
2377     {
2378         var target = e.getTarget();
2379         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2380             target = target.parentNode;
2381         }
2382         
2383         var ret = {
2384             position: '',
2385             cards : [],
2386             card_n : -1,
2387             items_n : -1,
2388             card : false 
2389         };
2390         
2391         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2392         // see if target is one of the 'cards'...
2393         
2394         
2395         //Roo.log(this.items.length);
2396         var pos = false;
2397         
2398         var last_card_n = 0;
2399         var cards_len  = 0;
2400         for (var i = 0;i< this.items.length;i++) {
2401             
2402             if (!this.items[i].el.hasClass('card')) {
2403                  continue;
2404             }
2405             pos = this.getDropPoint(e, this.items[i].el.dom);
2406             
2407             cards_len = ret.cards.length;
2408             //Roo.log(this.items[i].el.dom.id);
2409             ret.cards.push(this.items[i]);
2410             last_card_n  = i;
2411             if (ret.card_n < 0 && pos == 'above') {
2412                 ret.position = cards_len > 0 ? 'below' : pos;
2413                 ret.items_n = i > 0 ? i - 1 : 0;
2414                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2415                 ret.card = ret.cards[ret.card_n];
2416             }
2417         }
2418         if (!ret.cards.length) {
2419             ret.card = true;
2420             ret.position = 'below';
2421             ret.items_n;
2422             return ret;
2423         }
2424         // could not find a card.. stick it at the end..
2425         if (ret.card_n < 0) {
2426             ret.card_n = last_card_n;
2427             ret.card = ret.cards[last_card_n];
2428             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2429             ret.position = 'below';
2430         }
2431         
2432         if (this.items[ret.items_n].el == dragged_card_el) {
2433             return false;
2434         }
2435         
2436         if (ret.position == 'below') {
2437             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2438             
2439             if (card_after  && card_after.el == dragged_card_el) {
2440                 return false;
2441             }
2442             return ret;
2443         }
2444         
2445         // its's after ..
2446         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2447         
2448         if (card_before  && card_before.el == dragged_card_el) {
2449             return false;
2450         }
2451         
2452         return ret;
2453     },
2454     
2455     onNodeEnter : function(n, dd, e, data){
2456         return false;
2457     },
2458     onNodeOver : function(n, dd, e, data)
2459     {
2460        
2461         var target_info = this.getTargetFromEvent(e,data.source.el);
2462         if (target_info === false) {
2463             this.dropPlaceHolder('hide');
2464             return false;
2465         }
2466         Roo.log(['getTargetFromEvent', target_info ]);
2467         
2468          
2469         this.dropPlaceHolder('show', target_info,data);
2470         
2471         return false; 
2472     },
2473     onNodeOut : function(n, dd, e, data){
2474         this.dropPlaceHolder('hide');
2475      
2476     },
2477     onNodeDrop : function(n, dd, e, data)
2478     {
2479         
2480         // call drop - return false if
2481         
2482         // this could actually fail - if the Network drops..
2483         // we will ignore this at present..- client should probably reload
2484         // the whole set of cards if stuff like that fails.
2485         
2486         
2487         var info = this.getTargetFromEvent(e,data.source.el);
2488         if (info === false) {
2489             return false;
2490         }
2491         this.dropPlaceHolder('hide');
2492   
2493          
2494     
2495     
2496     
2497         this.acceptCard(data.source, info.position, info.card, info.items_n);
2498         return true;
2499          
2500     },
2501     firstChildCard : function()
2502     {
2503         for (var i = 0;i< this.items.length;i++) {
2504             
2505             if (!this.items[i].el.hasClass('card')) {
2506                  continue;
2507             }
2508             return this.items[i];
2509         }
2510         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2511     },
2512     /**
2513      * accept card
2514      *
2515      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2516      */
2517     acceptCard : function(move_card,  position, next_to_card )
2518     {
2519         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2520             return false;
2521         }
2522         
2523         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2524         
2525         move_card.parent().removeCard(move_card);
2526         
2527         
2528         var dom = move_card.el.dom;
2529         dom.style.width = ''; // clear with - which is set by drag.
2530         
2531         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2532             var cardel = next_to_card.el.dom;
2533             
2534             if (position == 'above' ) {
2535                 cardel.parentNode.insertBefore(dom, cardel);
2536             } else if (cardel.nextSibling) {
2537                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2538             } else {
2539                 cardel.parentNode.append(dom);
2540             }
2541         } else {
2542             // card container???
2543             this.containerEl.dom.append(dom);
2544         }
2545         
2546         //FIXME HANDLE card = true 
2547         
2548         // add this to the correct place in items.
2549         
2550         // remove Card from items.
2551         
2552        
2553         if (this.items.length) {
2554             var nitems = [];
2555             //Roo.log([info.items_n, info.position, this.items.length]);
2556             for (var i =0; i < this.items.length; i++) {
2557                 if (i == to_items_n && position == 'above') {
2558                     nitems.push(move_card);
2559                 }
2560                 nitems.push(this.items[i]);
2561                 if (i == to_items_n && position == 'below') {
2562                     nitems.push(move_card);
2563                 }
2564             }
2565             this.items = nitems;
2566             Roo.log(this.items);
2567         } else {
2568             this.items.push(move_card);
2569         }
2570         
2571         move_card.parentId = this.id;
2572         
2573         return true;
2574         
2575         
2576     },
2577     removeCard : function(c)
2578     {
2579         this.items = this.items.filter(function(e) { return e != c });
2580  
2581         var dom = c.el.dom;
2582         dom.parentNode.removeChild(dom);
2583         dom.style.width = ''; // clear with - which is set by drag.
2584         c.parentId = false;
2585         
2586     },
2587     
2588     /**    Decide whether to drop above or below a View node. */
2589     getDropPoint : function(e, n, dd)
2590     {
2591         if (dd) {
2592              return false;
2593         }
2594         if (n == this.containerEl.dom) {
2595             return "above";
2596         }
2597         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2598         var c = t + (b - t) / 2;
2599         var y = Roo.lib.Event.getPageY(e);
2600         if(y <= c) {
2601             return "above";
2602         }else{
2603             return "below";
2604         }
2605     },
2606     onToggleCollapse : function(e)
2607         {
2608         if (this.collapsed) {
2609             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2610             this.collapsableEl.addClass('show');
2611             this.collapsed = false;
2612             return;
2613         }
2614         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2615         this.collapsableEl.removeClass('show');
2616         this.collapsed = true;
2617         
2618     
2619     },
2620     
2621     onToggleRotate : function(e)
2622     {
2623         this.collapsableEl.removeClass('show');
2624         this.footerEl.removeClass('d-none');
2625         this.el.removeClass('roo-card-rotated');
2626         this.el.removeClass('d-none');
2627         if (this.rotated) {
2628             
2629             this.collapsableEl.addClass('show');
2630             this.rotated = false;
2631             this.fireEvent('rotate', this, this.rotated);
2632             return;
2633         }
2634         this.el.addClass('roo-card-rotated');
2635         this.footerEl.addClass('d-none');
2636         this.el.select('.roo-collapsable').removeClass('show');
2637         
2638         this.rotated = true;
2639         this.fireEvent('rotate', this, this.rotated);
2640     
2641     },
2642     
2643     dropPlaceHolder: function (action, info, data)
2644     {
2645         if (this.dropEl === false) {
2646             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2647             cls : 'd-none'
2648             },true);
2649         }
2650         this.dropEl.removeClass(['d-none', 'd-block']);        
2651         if (action == 'hide') {
2652             
2653             this.dropEl.addClass('d-none');
2654             return;
2655         }
2656         // FIXME - info.card == true!!!
2657         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2658         
2659         if (info.card !== true) {
2660             var cardel = info.card.el.dom;
2661             
2662             if (info.position == 'above') {
2663                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2664             } else if (cardel.nextSibling) {
2665                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2666             } else {
2667                 cardel.parentNode.append(this.dropEl.dom);
2668             }
2669         } else {
2670             // card container???
2671             this.containerEl.dom.append(this.dropEl.dom);
2672         }
2673         
2674         this.dropEl.addClass('d-block roo-card-dropzone');
2675         
2676         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2677         
2678         
2679     
2680     
2681     
2682     },
2683     setHeaderText: function(html)
2684     {
2685         this.header = html;
2686         if (this.headerContainerEl) {
2687             this.headerContainerEl.dom.innerHTML = html;
2688         }
2689     },
2690     onHeaderImageLoad : function(ev, he)
2691     {
2692         if (!this.header_image_fit_square) {
2693             return;
2694         }
2695         
2696         var hw = he.naturalHeight / he.naturalWidth;
2697         // wide image = < 0
2698         // tall image = > 1
2699         //var w = he.dom.naturalWidth;
2700         var ww = he.width;
2701         Roo.get(he).setX( 0 );
2702         if (hw > 1) {
2703             var nw = (ww * (1/hw));
2704             Roo.get(he).setSize( ww * (1/hw),  ww);
2705             Roo.get(he).setX( (ww - nw)/ 2);
2706         }
2707
2708     }
2709
2710     
2711 });
2712
2713 /*
2714  * - LGPL
2715  *
2716  * Card header - holder for the card header elements.
2717  * 
2718  */
2719
2720 /**
2721  * @class Roo.bootstrap.CardHeader
2722  * @extends Roo.bootstrap.Element
2723  * Bootstrap CardHeader class
2724  * @constructor
2725  * Create a new Card Header - that you can embed children into
2726  * @param {Object} config The config object
2727  */
2728
2729 Roo.bootstrap.CardHeader = function(config){
2730     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2731 };
2732
2733 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2734     
2735     
2736     container_method : 'getCardHeader' 
2737     
2738      
2739     
2740     
2741    
2742 });
2743
2744  
2745
2746  /*
2747  * - LGPL
2748  *
2749  * Card footer - holder for the card footer elements.
2750  * 
2751  */
2752
2753 /**
2754  * @class Roo.bootstrap.CardFooter
2755  * @extends Roo.bootstrap.Element
2756  * Bootstrap CardFooter class
2757  * @constructor
2758  * Create a new Card Footer - that you can embed children into
2759  * @param {Object} config The config object
2760  */
2761
2762 Roo.bootstrap.CardFooter = function(config){
2763     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2764 };
2765
2766 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2767     
2768     
2769     container_method : 'getCardFooter' 
2770     
2771      
2772     
2773     
2774    
2775 });
2776
2777  
2778
2779  /*
2780  * - LGPL
2781  *
2782  * Card header - holder for the card header elements.
2783  * 
2784  */
2785
2786 /**
2787  * @class Roo.bootstrap.CardImageTop
2788  * @extends Roo.bootstrap.Element
2789  * Bootstrap CardImageTop class
2790  * @constructor
2791  * Create a new Card Image Top container
2792  * @param {Object} config The config object
2793  */
2794
2795 Roo.bootstrap.CardImageTop = function(config){
2796     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2797 };
2798
2799 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2800     
2801    
2802     container_method : 'getCardImageTop' 
2803     
2804      
2805     
2806    
2807 });
2808
2809  
2810
2811  /*
2812  * - LGPL
2813  *
2814  * image
2815  * 
2816  */
2817
2818
2819 /**
2820  * @class Roo.bootstrap.Img
2821  * @extends Roo.bootstrap.Component
2822  * Bootstrap Img class
2823  * @cfg {Boolean} imgResponsive false | true
2824  * @cfg {String} border rounded | circle | thumbnail
2825  * @cfg {String} src image source
2826  * @cfg {String} alt image alternative text
2827  * @cfg {String} href a tag href
2828  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2829  * @cfg {String} xsUrl xs image source
2830  * @cfg {String} smUrl sm image source
2831  * @cfg {String} mdUrl md image source
2832  * @cfg {String} lgUrl lg image source
2833  * 
2834  * @constructor
2835  * Create a new Input
2836  * @param {Object} config The config object
2837  */
2838
2839 Roo.bootstrap.Img = function(config){
2840     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2841     
2842     this.addEvents({
2843         // img events
2844         /**
2845          * @event click
2846          * The img click event for the img.
2847          * @param {Roo.EventObject} e
2848          */
2849         "click" : true
2850     });
2851 };
2852
2853 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2854     
2855     imgResponsive: true,
2856     border: '',
2857     src: 'about:blank',
2858     href: false,
2859     target: false,
2860     xsUrl: '',
2861     smUrl: '',
2862     mdUrl: '',
2863     lgUrl: '',
2864
2865     getAutoCreate : function()
2866     {   
2867         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2868             return this.createSingleImg();
2869         }
2870         
2871         var cfg = {
2872             tag: 'div',
2873             cls: 'roo-image-responsive-group',
2874             cn: []
2875         };
2876         var _this = this;
2877         
2878         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2879             
2880             if(!_this[size + 'Url']){
2881                 return;
2882             }
2883             
2884             var img = {
2885                 tag: 'img',
2886                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2887                 html: _this.html || cfg.html,
2888                 src: _this[size + 'Url']
2889             };
2890             
2891             img.cls += ' roo-image-responsive-' + size;
2892             
2893             var s = ['xs', 'sm', 'md', 'lg'];
2894             
2895             s.splice(s.indexOf(size), 1);
2896             
2897             Roo.each(s, function(ss){
2898                 img.cls += ' hidden-' + ss;
2899             });
2900             
2901             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2902                 cfg.cls += ' img-' + _this.border;
2903             }
2904             
2905             if(_this.alt){
2906                 cfg.alt = _this.alt;
2907             }
2908             
2909             if(_this.href){
2910                 var a = {
2911                     tag: 'a',
2912                     href: _this.href,
2913                     cn: [
2914                         img
2915                     ]
2916                 };
2917
2918                 if(this.target){
2919                     a.target = _this.target;
2920                 }
2921             }
2922             
2923             cfg.cn.push((_this.href) ? a : img);
2924             
2925         });
2926         
2927         return cfg;
2928     },
2929     
2930     createSingleImg : function()
2931     {
2932         var cfg = {
2933             tag: 'img',
2934             cls: (this.imgResponsive) ? 'img-responsive' : '',
2935             html : null,
2936             src : 'about:blank'  // just incase src get's set to undefined?!?
2937         };
2938         
2939         cfg.html = this.html || cfg.html;
2940         
2941         cfg.src = this.src || cfg.src;
2942         
2943         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2944             cfg.cls += ' img-' + this.border;
2945         }
2946         
2947         if(this.alt){
2948             cfg.alt = this.alt;
2949         }
2950         
2951         if(this.href){
2952             var a = {
2953                 tag: 'a',
2954                 href: this.href,
2955                 cn: [
2956                     cfg
2957                 ]
2958             };
2959             
2960             if(this.target){
2961                 a.target = this.target;
2962             }
2963             
2964         }
2965         
2966         return (this.href) ? a : cfg;
2967     },
2968     
2969     initEvents: function() 
2970     {
2971         if(!this.href){
2972             this.el.on('click', this.onClick, this);
2973         }
2974         
2975     },
2976     
2977     onClick : function(e)
2978     {
2979         Roo.log('img onclick');
2980         this.fireEvent('click', this, e);
2981     },
2982     /**
2983      * Sets the url of the image - used to update it
2984      * @param {String} url the url of the image
2985      */
2986     
2987     setSrc : function(url)
2988     {
2989         this.src =  url;
2990         
2991         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2992             this.el.dom.src =  url;
2993             return;
2994         }
2995         
2996         this.el.select('img', true).first().dom.src =  url;
2997     }
2998     
2999     
3000    
3001 });
3002
3003  /*
3004  * - LGPL
3005  *
3006  * image
3007  * 
3008  */
3009
3010
3011 /**
3012  * @class Roo.bootstrap.Link
3013  * @extends Roo.bootstrap.Component
3014  * Bootstrap Link Class
3015  * @cfg {String} alt image alternative text
3016  * @cfg {String} href a tag href
3017  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3018  * @cfg {String} html the content of the link.
3019  * @cfg {String} anchor name for the anchor link
3020  * @cfg {String} fa - favicon
3021
3022  * @cfg {Boolean} preventDefault (true | false) default false
3023
3024  * 
3025  * @constructor
3026  * Create a new Input
3027  * @param {Object} config The config object
3028  */
3029
3030 Roo.bootstrap.Link = function(config){
3031     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3032     
3033     this.addEvents({
3034         // img events
3035         /**
3036          * @event click
3037          * The img click event for the img.
3038          * @param {Roo.EventObject} e
3039          */
3040         "click" : true
3041     });
3042 };
3043
3044 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3045     
3046     href: false,
3047     target: false,
3048     preventDefault: false,
3049     anchor : false,
3050     alt : false,
3051     fa: false,
3052
3053
3054     getAutoCreate : function()
3055     {
3056         var html = this.html || '';
3057         
3058         if (this.fa !== false) {
3059             html = '<i class="fa fa-' + this.fa + '"></i>';
3060         }
3061         var cfg = {
3062             tag: 'a'
3063         };
3064         // anchor's do not require html/href...
3065         if (this.anchor === false) {
3066             cfg.html = html;
3067             cfg.href = this.href || '#';
3068         } else {
3069             cfg.name = this.anchor;
3070             if (this.html !== false || this.fa !== false) {
3071                 cfg.html = html;
3072             }
3073             if (this.href !== false) {
3074                 cfg.href = this.href;
3075             }
3076         }
3077         
3078         if(this.alt !== false){
3079             cfg.alt = this.alt;
3080         }
3081         
3082         
3083         if(this.target !== false) {
3084             cfg.target = this.target;
3085         }
3086         
3087         return cfg;
3088     },
3089     
3090     initEvents: function() {
3091         
3092         if(!this.href || this.preventDefault){
3093             this.el.on('click', this.onClick, this);
3094         }
3095     },
3096     
3097     onClick : function(e)
3098     {
3099         if(this.preventDefault){
3100             e.preventDefault();
3101         }
3102         //Roo.log('img onclick');
3103         this.fireEvent('click', this, e);
3104     }
3105    
3106 });
3107
3108  /*
3109  * - LGPL
3110  *
3111  * header
3112  * 
3113  */
3114
3115 /**
3116  * @class Roo.bootstrap.Header
3117  * @extends Roo.bootstrap.Component
3118  * Bootstrap Header class
3119  * @cfg {String} html content of header
3120  * @cfg {Number} level (1|2|3|4|5|6) default 1
3121  * 
3122  * @constructor
3123  * Create a new Header
3124  * @param {Object} config The config object
3125  */
3126
3127
3128 Roo.bootstrap.Header  = function(config){
3129     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3130 };
3131
3132 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3133     
3134     //href : false,
3135     html : false,
3136     level : 1,
3137     
3138     
3139     
3140     getAutoCreate : function(){
3141         
3142         
3143         
3144         var cfg = {
3145             tag: 'h' + (1 *this.level),
3146             html: this.html || ''
3147         } ;
3148         
3149         return cfg;
3150     }
3151    
3152 });
3153
3154  
3155
3156  /*
3157  * Based on:
3158  * Ext JS Library 1.1.1
3159  * Copyright(c) 2006-2007, Ext JS, LLC.
3160  *
3161  * Originally Released Under LGPL - original licence link has changed is not relivant.
3162  *
3163  * Fork - LGPL
3164  * <script type="text/javascript">
3165  */
3166  
3167 /**
3168  * @class Roo.bootstrap.MenuMgr
3169  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3170  * @singleton
3171  */
3172 Roo.bootstrap.MenuMgr = function(){
3173    var menus, active, groups = {}, attached = false, lastShow = new Date();
3174
3175    // private - called when first menu is created
3176    function init(){
3177        menus = {};
3178        active = new Roo.util.MixedCollection();
3179        Roo.get(document).addKeyListener(27, function(){
3180            if(active.length > 0){
3181                hideAll();
3182            }
3183        });
3184    }
3185
3186    // private
3187    function hideAll(){
3188        if(active && active.length > 0){
3189            var c = active.clone();
3190            c.each(function(m){
3191                m.hide();
3192            });
3193        }
3194    }
3195
3196    // private
3197    function onHide(m){
3198        active.remove(m);
3199        if(active.length < 1){
3200            Roo.get(document).un("mouseup", onMouseDown);
3201             
3202            attached = false;
3203        }
3204    }
3205
3206    // private
3207    function onShow(m){
3208        var last = active.last();
3209        lastShow = new Date();
3210        active.add(m);
3211        if(!attached){
3212           Roo.get(document).on("mouseup", onMouseDown);
3213            
3214            attached = true;
3215        }
3216        if(m.parentMenu){
3217           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3218           m.parentMenu.activeChild = m;
3219        }else if(last && last.isVisible()){
3220           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3221        }
3222    }
3223
3224    // private
3225    function onBeforeHide(m){
3226        if(m.activeChild){
3227            m.activeChild.hide();
3228        }
3229        if(m.autoHideTimer){
3230            clearTimeout(m.autoHideTimer);
3231            delete m.autoHideTimer;
3232        }
3233    }
3234
3235    // private
3236    function onBeforeShow(m){
3237        var pm = m.parentMenu;
3238        if(!pm && !m.allowOtherMenus){
3239            hideAll();
3240        }else if(pm && pm.activeChild && active != m){
3241            pm.activeChild.hide();
3242        }
3243    }
3244
3245    // private this should really trigger on mouseup..
3246    function onMouseDown(e){
3247         Roo.log("on Mouse Up");
3248         
3249         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3250             Roo.log("MenuManager hideAll");
3251             hideAll();
3252             e.stopEvent();
3253         }
3254         
3255         
3256    }
3257
3258    // private
3259    function onBeforeCheck(mi, state){
3260        if(state){
3261            var g = groups[mi.group];
3262            for(var i = 0, l = g.length; i < l; i++){
3263                if(g[i] != mi){
3264                    g[i].setChecked(false);
3265                }
3266            }
3267        }
3268    }
3269
3270    return {
3271
3272        /**
3273         * Hides all menus that are currently visible
3274         */
3275        hideAll : function(){
3276             hideAll();  
3277        },
3278
3279        // private
3280        register : function(menu){
3281            if(!menus){
3282                init();
3283            }
3284            menus[menu.id] = menu;
3285            menu.on("beforehide", onBeforeHide);
3286            menu.on("hide", onHide);
3287            menu.on("beforeshow", onBeforeShow);
3288            menu.on("show", onShow);
3289            var g = menu.group;
3290            if(g && menu.events["checkchange"]){
3291                if(!groups[g]){
3292                    groups[g] = [];
3293                }
3294                groups[g].push(menu);
3295                menu.on("checkchange", onCheck);
3296            }
3297        },
3298
3299         /**
3300          * Returns a {@link Roo.menu.Menu} object
3301          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3302          * be used to generate and return a new Menu instance.
3303          */
3304        get : function(menu){
3305            if(typeof menu == "string"){ // menu id
3306                return menus[menu];
3307            }else if(menu.events){  // menu instance
3308                return menu;
3309            }
3310            /*else if(typeof menu.length == 'number'){ // array of menu items?
3311                return new Roo.bootstrap.Menu({items:menu});
3312            }else{ // otherwise, must be a config
3313                return new Roo.bootstrap.Menu(menu);
3314            }
3315            */
3316            return false;
3317        },
3318
3319        // private
3320        unregister : function(menu){
3321            delete menus[menu.id];
3322            menu.un("beforehide", onBeforeHide);
3323            menu.un("hide", onHide);
3324            menu.un("beforeshow", onBeforeShow);
3325            menu.un("show", onShow);
3326            var g = menu.group;
3327            if(g && menu.events["checkchange"]){
3328                groups[g].remove(menu);
3329                menu.un("checkchange", onCheck);
3330            }
3331        },
3332
3333        // private
3334        registerCheckable : function(menuItem){
3335            var g = menuItem.group;
3336            if(g){
3337                if(!groups[g]){
3338                    groups[g] = [];
3339                }
3340                groups[g].push(menuItem);
3341                menuItem.on("beforecheckchange", onBeforeCheck);
3342            }
3343        },
3344
3345        // private
3346        unregisterCheckable : function(menuItem){
3347            var g = menuItem.group;
3348            if(g){
3349                groups[g].remove(menuItem);
3350                menuItem.un("beforecheckchange", onBeforeCheck);
3351            }
3352        }
3353    };
3354 }();/*
3355  * - LGPL
3356  *
3357  * menu
3358  * 
3359  */
3360
3361 /**
3362  * @class Roo.bootstrap.Menu
3363  * @extends Roo.bootstrap.Component
3364  * Bootstrap Menu class - container for MenuItems
3365  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3366  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3367  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3368  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3369  * 
3370  * @constructor
3371  * Create a new Menu
3372  * @param {Object} config The config object
3373  */
3374
3375
3376 Roo.bootstrap.Menu = function(config){
3377     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3378     if (this.registerMenu && this.type != 'treeview')  {
3379         Roo.bootstrap.MenuMgr.register(this);
3380     }
3381     
3382     
3383     this.addEvents({
3384         /**
3385          * @event beforeshow
3386          * Fires before this menu is displayed (return false to block)
3387          * @param {Roo.menu.Menu} this
3388          */
3389         beforeshow : true,
3390         /**
3391          * @event beforehide
3392          * Fires before this menu is hidden (return false to block)
3393          * @param {Roo.menu.Menu} this
3394          */
3395         beforehide : true,
3396         /**
3397          * @event show
3398          * Fires after this menu is displayed
3399          * @param {Roo.menu.Menu} this
3400          */
3401         show : true,
3402         /**
3403          * @event hide
3404          * Fires after this menu is hidden
3405          * @param {Roo.menu.Menu} this
3406          */
3407         hide : true,
3408         /**
3409          * @event click
3410          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3411          * @param {Roo.menu.Menu} this
3412          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3413          * @param {Roo.EventObject} e
3414          */
3415         click : true,
3416         /**
3417          * @event mouseover
3418          * Fires when the mouse is hovering over this menu
3419          * @param {Roo.menu.Menu} this
3420          * @param {Roo.EventObject} e
3421          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3422          */
3423         mouseover : true,
3424         /**
3425          * @event mouseout
3426          * Fires when the mouse exits this menu
3427          * @param {Roo.menu.Menu} this
3428          * @param {Roo.EventObject} e
3429          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3430          */
3431         mouseout : true,
3432         /**
3433          * @event itemclick
3434          * Fires when a menu item contained in this menu is clicked
3435          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3436          * @param {Roo.EventObject} e
3437          */
3438         itemclick: true
3439     });
3440     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3441 };
3442
3443 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3444     
3445    /// html : false,
3446     //align : '',
3447     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3448     type: false,
3449     /**
3450      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3451      */
3452     registerMenu : true,
3453     
3454     menuItems :false, // stores the menu items..
3455     
3456     hidden:true,
3457         
3458     parentMenu : false,
3459     
3460     stopEvent : true,
3461     
3462     isLink : false,
3463     
3464     getChildContainer : function() {
3465         return this.el;  
3466     },
3467     
3468     getAutoCreate : function(){
3469          
3470         //if (['right'].indexOf(this.align)!==-1) {
3471         //    cfg.cn[1].cls += ' pull-right'
3472         //}
3473         
3474         
3475         var cfg = {
3476             tag : 'ul',
3477             cls : 'dropdown-menu' ,
3478             style : 'z-index:1000'
3479             
3480         };
3481         
3482         if (this.type === 'submenu') {
3483             cfg.cls = 'submenu active';
3484         }
3485         if (this.type === 'treeview') {
3486             cfg.cls = 'treeview-menu';
3487         }
3488         
3489         return cfg;
3490     },
3491     initEvents : function() {
3492         
3493        // Roo.log("ADD event");
3494        // Roo.log(this.triggerEl.dom);
3495         
3496         this.triggerEl.on('click', this.onTriggerClick, this);
3497         
3498         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3499         
3500         
3501         if (this.triggerEl.hasClass('nav-item')) {
3502             // dropdown toggle on the 'a' in BS4?
3503             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3504         } else {
3505             this.triggerEl.addClass('dropdown-toggle');
3506         }
3507         if (Roo.isTouch) {
3508             this.el.on('touchstart'  , this.onTouch, this);
3509         }
3510         this.el.on('click' , this.onClick, this);
3511
3512         this.el.on("mouseover", this.onMouseOver, this);
3513         this.el.on("mouseout", this.onMouseOut, this);
3514         
3515     },
3516     
3517     findTargetItem : function(e)
3518     {
3519         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3520         if(!t){
3521             return false;
3522         }
3523         //Roo.log(t);         Roo.log(t.id);
3524         if(t && t.id){
3525             //Roo.log(this.menuitems);
3526             return this.menuitems.get(t.id);
3527             
3528             //return this.items.get(t.menuItemId);
3529         }
3530         
3531         return false;
3532     },
3533     
3534     onTouch : function(e) 
3535     {
3536         Roo.log("menu.onTouch");
3537         //e.stopEvent(); this make the user popdown broken
3538         this.onClick(e);
3539     },
3540     
3541     onClick : function(e)
3542     {
3543         Roo.log("menu.onClick");
3544         
3545         var t = this.findTargetItem(e);
3546         if(!t || t.isContainer){
3547             return;
3548         }
3549         Roo.log(e);
3550         /*
3551         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3552             if(t == this.activeItem && t.shouldDeactivate(e)){
3553                 this.activeItem.deactivate();
3554                 delete this.activeItem;
3555                 return;
3556             }
3557             if(t.canActivate){
3558                 this.setActiveItem(t, true);
3559             }
3560             return;
3561             
3562             
3563         }
3564         */
3565        
3566         Roo.log('pass click event');
3567         
3568         t.onClick(e);
3569         
3570         this.fireEvent("click", this, t, e);
3571         
3572         var _this = this;
3573         
3574         if(!t.href.length || t.href == '#'){
3575             (function() { _this.hide(); }).defer(100);
3576         }
3577         
3578     },
3579     
3580     onMouseOver : function(e){
3581         var t  = this.findTargetItem(e);
3582         //Roo.log(t);
3583         //if(t){
3584         //    if(t.canActivate && !t.disabled){
3585         //        this.setActiveItem(t, true);
3586         //    }
3587         //}
3588         
3589         this.fireEvent("mouseover", this, e, t);
3590     },
3591     isVisible : function(){
3592         return !this.hidden;
3593     },
3594     onMouseOut : function(e){
3595         var t  = this.findTargetItem(e);
3596         
3597         //if(t ){
3598         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3599         //        this.activeItem.deactivate();
3600         //        delete this.activeItem;
3601         //    }
3602         //}
3603         this.fireEvent("mouseout", this, e, t);
3604     },
3605     
3606     
3607     /**
3608      * Displays this menu relative to another element
3609      * @param {String/HTMLElement/Roo.Element} element The element to align to
3610      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3611      * the element (defaults to this.defaultAlign)
3612      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3613      */
3614     show : function(el, pos, parentMenu)
3615     {
3616         if (false === this.fireEvent("beforeshow", this)) {
3617             Roo.log("show canceled");
3618             return;
3619         }
3620         this.parentMenu = parentMenu;
3621         if(!this.el){
3622             this.render();
3623         }
3624         
3625         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3626     },
3627      /**
3628      * Displays this menu at a specific xy position
3629      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3630      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3631      */
3632     showAt : function(xy, parentMenu, /* private: */_e){
3633         this.parentMenu = parentMenu;
3634         if(!this.el){
3635             this.render();
3636         }
3637         if(_e !== false){
3638             this.fireEvent("beforeshow", this);
3639             //xy = this.el.adjustForConstraints(xy);
3640         }
3641         
3642         //this.el.show();
3643         this.hideMenuItems();
3644         this.hidden = false;
3645         this.triggerEl.addClass('open');
3646         this.el.addClass('show');
3647         
3648         // reassign x when hitting right
3649         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3650             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3651         }
3652         
3653         // reassign y when hitting bottom
3654         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3655             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3656         }
3657         
3658         // but the list may align on trigger left or trigger top... should it be a properity?
3659         
3660         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3661             this.el.setXY(xy);
3662         }
3663         
3664         this.focus();
3665         this.fireEvent("show", this);
3666     },
3667     
3668     focus : function(){
3669         return;
3670         if(!this.hidden){
3671             this.doFocus.defer(50, this);
3672         }
3673     },
3674
3675     doFocus : function(){
3676         if(!this.hidden){
3677             this.focusEl.focus();
3678         }
3679     },
3680
3681     /**
3682      * Hides this menu and optionally all parent menus
3683      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3684      */
3685     hide : function(deep)
3686     {
3687         if (false === this.fireEvent("beforehide", this)) {
3688             Roo.log("hide canceled");
3689             return;
3690         }
3691         this.hideMenuItems();
3692         if(this.el && this.isVisible()){
3693            
3694             if(this.activeItem){
3695                 this.activeItem.deactivate();
3696                 this.activeItem = null;
3697             }
3698             this.triggerEl.removeClass('open');;
3699             this.el.removeClass('show');
3700             this.hidden = true;
3701             this.fireEvent("hide", this);
3702         }
3703         if(deep === true && this.parentMenu){
3704             this.parentMenu.hide(true);
3705         }
3706     },
3707     
3708     onTriggerClick : function(e)
3709     {
3710         Roo.log('trigger click');
3711         
3712         var target = e.getTarget();
3713         
3714         Roo.log(target.nodeName.toLowerCase());
3715         
3716         if(target.nodeName.toLowerCase() === 'i'){
3717             e.preventDefault();
3718         }
3719         
3720     },
3721     
3722     onTriggerPress  : function(e)
3723     {
3724         Roo.log('trigger press');
3725         //Roo.log(e.getTarget());
3726        // Roo.log(this.triggerEl.dom);
3727        
3728         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3729         var pel = Roo.get(e.getTarget());
3730         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3731             Roo.log('is treeview or dropdown?');
3732             return;
3733         }
3734         
3735         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3736             return;
3737         }
3738         
3739         if (this.isVisible()) {
3740             Roo.log('hide');
3741             this.hide();
3742         } else {
3743             Roo.log('show');
3744             this.show(this.triggerEl, '?', false);
3745         }
3746         
3747         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3748             e.stopEvent();
3749         }
3750         
3751     },
3752        
3753     
3754     hideMenuItems : function()
3755     {
3756         Roo.log("hide Menu Items");
3757         if (!this.el) { 
3758             return;
3759         }
3760         
3761         this.el.select('.open',true).each(function(aa) {
3762             
3763             aa.removeClass('open');
3764          
3765         });
3766     },
3767     addxtypeChild : function (tree, cntr) {
3768         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3769           
3770         this.menuitems.add(comp);
3771         return comp;
3772
3773     },
3774     getEl : function()
3775     {
3776         Roo.log(this.el);
3777         return this.el;
3778     },
3779     
3780     clear : function()
3781     {
3782         this.getEl().dom.innerHTML = '';
3783         this.menuitems.clear();
3784     }
3785 });
3786
3787  
3788  /*
3789  * - LGPL
3790  *
3791  * menu item
3792  * 
3793  */
3794
3795
3796 /**
3797  * @class Roo.bootstrap.MenuItem
3798  * @extends Roo.bootstrap.Component
3799  * Bootstrap MenuItem class
3800  * @cfg {String} html the menu label
3801  * @cfg {String} href the link
3802  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3803  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3804  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3805  * @cfg {String} fa favicon to show on left of menu item.
3806  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3807  * 
3808  * 
3809  * @constructor
3810  * Create a new MenuItem
3811  * @param {Object} config The config object
3812  */
3813
3814
3815 Roo.bootstrap.MenuItem = function(config){
3816     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3817     this.addEvents({
3818         // raw events
3819         /**
3820          * @event click
3821          * The raw click event for the entire grid.
3822          * @param {Roo.bootstrap.MenuItem} this
3823          * @param {Roo.EventObject} e
3824          */
3825         "click" : true
3826     });
3827 };
3828
3829 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3830     
3831     href : false,
3832     html : false,
3833     preventDefault: false,
3834     isContainer : false,
3835     active : false,
3836     fa: false,
3837     
3838     getAutoCreate : function(){
3839         
3840         if(this.isContainer){
3841             return {
3842                 tag: 'li',
3843                 cls: 'dropdown-menu-item '
3844             };
3845         }
3846         var ctag = {
3847             tag: 'span',
3848             html: 'Link'
3849         };
3850         
3851         var anc = {
3852             tag : 'a',
3853             cls : 'dropdown-item',
3854             href : '#',
3855             cn : [  ]
3856         };
3857         
3858         if (this.fa !== false) {
3859             anc.cn.push({
3860                 tag : 'i',
3861                 cls : 'fa fa-' + this.fa
3862             });
3863         }
3864         
3865         anc.cn.push(ctag);
3866         
3867         
3868         var cfg= {
3869             tag: 'li',
3870             cls: 'dropdown-menu-item',
3871             cn: [ anc ]
3872         };
3873         if (this.parent().type == 'treeview') {
3874             cfg.cls = 'treeview-menu';
3875         }
3876         if (this.active) {
3877             cfg.cls += ' active';
3878         }
3879         
3880         
3881         
3882         anc.href = this.href || cfg.cn[0].href ;
3883         ctag.html = this.html || cfg.cn[0].html ;
3884         return cfg;
3885     },
3886     
3887     initEvents: function()
3888     {
3889         if (this.parent().type == 'treeview') {
3890             this.el.select('a').on('click', this.onClick, this);
3891         }
3892         
3893         if (this.menu) {
3894             this.menu.parentType = this.xtype;
3895             this.menu.triggerEl = this.el;
3896             this.menu = this.addxtype(Roo.apply({}, this.menu));
3897         }
3898         
3899     },
3900     onClick : function(e)
3901     {
3902         Roo.log('item on click ');
3903         
3904         if(this.preventDefault){
3905             e.preventDefault();
3906         }
3907         //this.parent().hideMenuItems();
3908         
3909         this.fireEvent('click', this, e);
3910     },
3911     getEl : function()
3912     {
3913         return this.el;
3914     } 
3915 });
3916
3917  
3918
3919  /*
3920  * - LGPL
3921  *
3922  * menu separator
3923  * 
3924  */
3925
3926
3927 /**
3928  * @class Roo.bootstrap.MenuSeparator
3929  * @extends Roo.bootstrap.Component
3930  * Bootstrap MenuSeparator class
3931  * 
3932  * @constructor
3933  * Create a new MenuItem
3934  * @param {Object} config The config object
3935  */
3936
3937
3938 Roo.bootstrap.MenuSeparator = function(config){
3939     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3940 };
3941
3942 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3943     
3944     getAutoCreate : function(){
3945         var cfg = {
3946             cls: 'divider',
3947             tag : 'li'
3948         };
3949         
3950         return cfg;
3951     }
3952    
3953 });
3954
3955  
3956
3957  
3958 /*
3959 * Licence: LGPL
3960 */
3961
3962 /**
3963  * @class Roo.bootstrap.Modal
3964  * @extends Roo.bootstrap.Component
3965  * Bootstrap Modal class
3966  * @cfg {String} title Title of dialog
3967  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3968  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3969  * @cfg {Boolean} specificTitle default false
3970  * @cfg {Array} buttons Array of buttons or standard button set..
3971  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3972  * @cfg {Boolean} animate default true
3973  * @cfg {Boolean} allow_close default true
3974  * @cfg {Boolean} fitwindow default false
3975  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3976  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3977  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3978  * @cfg {String} size (sm|lg|xl) default empty
3979  * @cfg {Number} max_width set the max width of modal
3980  * @cfg {Boolean} editableTitle can the title be edited
3981
3982  *
3983  *
3984  * @constructor
3985  * Create a new Modal Dialog
3986  * @param {Object} config The config object
3987  */
3988
3989 Roo.bootstrap.Modal = function(config){
3990     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3991     this.addEvents({
3992         // raw events
3993         /**
3994          * @event btnclick
3995          * The raw btnclick event for the button
3996          * @param {Roo.EventObject} e
3997          */
3998         "btnclick" : true,
3999         /**
4000          * @event resize
4001          * Fire when dialog resize
4002          * @param {Roo.bootstrap.Modal} this
4003          * @param {Roo.EventObject} e
4004          */
4005         "resize" : true,
4006         /**
4007          * @event titlechanged
4008          * Fire when the editable title has been changed
4009          * @param {Roo.bootstrap.Modal} this
4010          * @param {Roo.EventObject} value
4011          */
4012         "titlechanged" : true 
4013         
4014     });
4015     this.buttons = this.buttons || [];
4016
4017     if (this.tmpl) {
4018         this.tmpl = Roo.factory(this.tmpl);
4019     }
4020
4021 };
4022
4023 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4024
4025     title : 'test dialog',
4026
4027     buttons : false,
4028
4029     // set on load...
4030
4031     html: false,
4032
4033     tmp: false,
4034
4035     specificTitle: false,
4036
4037     buttonPosition: 'right',
4038
4039     allow_close : true,
4040
4041     animate : true,
4042
4043     fitwindow: false,
4044     
4045      // private
4046     dialogEl: false,
4047     bodyEl:  false,
4048     footerEl:  false,
4049     titleEl:  false,
4050     closeEl:  false,
4051
4052     size: '',
4053     
4054     max_width: 0,
4055     
4056     max_height: 0,
4057     
4058     fit_content: false,
4059     editableTitle  : false,
4060
4061     onRender : function(ct, position)
4062     {
4063         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4064
4065         if(!this.el){
4066             var cfg = Roo.apply({},  this.getAutoCreate());
4067             cfg.id = Roo.id();
4068             //if(!cfg.name){
4069             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4070             //}
4071             //if (!cfg.name.length) {
4072             //    delete cfg.name;
4073            // }
4074             if (this.cls) {
4075                 cfg.cls += ' ' + this.cls;
4076             }
4077             if (this.style) {
4078                 cfg.style = this.style;
4079             }
4080             this.el = Roo.get(document.body).createChild(cfg, position);
4081         }
4082         //var type = this.el.dom.type;
4083
4084
4085         if(this.tabIndex !== undefined){
4086             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4087         }
4088
4089         this.dialogEl = this.el.select('.modal-dialog',true).first();
4090         this.bodyEl = this.el.select('.modal-body',true).first();
4091         this.closeEl = this.el.select('.modal-header .close', true).first();
4092         this.headerEl = this.el.select('.modal-header',true).first();
4093         this.titleEl = this.el.select('.modal-title',true).first();
4094         this.footerEl = this.el.select('.modal-footer',true).first();
4095
4096         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4097         
4098         //this.el.addClass("x-dlg-modal");
4099
4100         if (this.buttons.length) {
4101             Roo.each(this.buttons, function(bb) {
4102                 var b = Roo.apply({}, bb);
4103                 b.xns = b.xns || Roo.bootstrap;
4104                 b.xtype = b.xtype || 'Button';
4105                 if (typeof(b.listeners) == 'undefined') {
4106                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4107                 }
4108
4109                 var btn = Roo.factory(b);
4110
4111                 btn.render(this.getButtonContainer());
4112
4113             },this);
4114         }
4115         // render the children.
4116         var nitems = [];
4117
4118         if(typeof(this.items) != 'undefined'){
4119             var items = this.items;
4120             delete this.items;
4121
4122             for(var i =0;i < items.length;i++) {
4123                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4124             }
4125         }
4126
4127         this.items = nitems;
4128
4129         // where are these used - they used to be body/close/footer
4130
4131
4132         this.initEvents();
4133         //this.el.addClass([this.fieldClass, this.cls]);
4134
4135     },
4136
4137     getAutoCreate : function()
4138     {
4139         // we will default to modal-body-overflow - might need to remove or make optional later.
4140         var bdy = {
4141                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4142                 html : this.html || ''
4143         };
4144
4145         var title = {
4146             tag: 'h5',
4147             cls : 'modal-title',
4148             html : this.title
4149         };
4150
4151         if(this.specificTitle){ // WTF is this?
4152             title = this.title;
4153         }
4154
4155         var header = [];
4156         if (this.allow_close && Roo.bootstrap.version == 3) {
4157             header.push({
4158                 tag: 'button',
4159                 cls : 'close',
4160                 html : '&times'
4161             });
4162         }
4163
4164         header.push(title);
4165
4166         if (this.editableTitle) {
4167             header.push({
4168                 cls: 'form-control roo-editable-title d-none',
4169                 tag: 'input',
4170                 type: 'text'
4171             });
4172         }
4173         
4174         if (this.allow_close && Roo.bootstrap.version == 4) {
4175             header.push({
4176                 tag: 'button',
4177                 cls : 'close',
4178                 html : '&times'
4179             });
4180         }
4181         
4182         var size = '';
4183
4184         if(this.size.length){
4185             size = 'modal-' + this.size;
4186         }
4187         
4188         var footer = Roo.bootstrap.version == 3 ?
4189             {
4190                 cls : 'modal-footer',
4191                 cn : [
4192                     {
4193                         tag: 'div',
4194                         cls: 'btn-' + this.buttonPosition
4195                     }
4196                 ]
4197
4198             } :
4199             {  // BS4 uses mr-auto on left buttons....
4200                 cls : 'modal-footer'
4201             };
4202
4203             
4204
4205         
4206         
4207         var modal = {
4208             cls: "modal",
4209              cn : [
4210                 {
4211                     cls: "modal-dialog " + size,
4212                     cn : [
4213                         {
4214                             cls : "modal-content",
4215                             cn : [
4216                                 {
4217                                     cls : 'modal-header',
4218                                     cn : header
4219                                 },
4220                                 bdy,
4221                                 footer
4222                             ]
4223
4224                         }
4225                     ]
4226
4227                 }
4228             ]
4229         };
4230
4231         if(this.animate){
4232             modal.cls += ' fade';
4233         }
4234
4235         return modal;
4236
4237     },
4238     getChildContainer : function() {
4239
4240          return this.bodyEl;
4241
4242     },
4243     getButtonContainer : function() {
4244         
4245          return Roo.bootstrap.version == 4 ?
4246             this.el.select('.modal-footer',true).first()
4247             : this.el.select('.modal-footer div',true).first();
4248
4249     },
4250     initEvents : function()
4251     {
4252         if (this.allow_close) {
4253             this.closeEl.on('click', this.hide, this);
4254         }
4255         Roo.EventManager.onWindowResize(this.resize, this, true);
4256         if (this.editableTitle) {
4257             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4258             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4259             this.headerEditEl.on('keyup', function(e) {
4260                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4261                         this.toggleHeaderInput(false)
4262                     }
4263                 }, this);
4264             this.headerEditEl.on('blur', function(e) {
4265                 this.toggleHeaderInput(false)
4266             },this);
4267         }
4268
4269     },
4270   
4271
4272     resize : function()
4273     {
4274         this.maskEl.setSize(
4275             Roo.lib.Dom.getViewWidth(true),
4276             Roo.lib.Dom.getViewHeight(true)
4277         );
4278         
4279         if (this.fitwindow) {
4280             
4281            this.dialogEl.setStyle( { 'max-width' : '100%' });
4282             this.setSize(
4283                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4284                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4285             );
4286             return;
4287         }
4288         
4289         if(this.max_width !== 0) {
4290             
4291             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4292             
4293             if(this.height) {
4294                 this.setSize(w, this.height);
4295                 return;
4296             }
4297             
4298             if(this.max_height) {
4299                 this.setSize(w,Math.min(
4300                     this.max_height,
4301                     Roo.lib.Dom.getViewportHeight(true) - 60
4302                 ));
4303                 
4304                 return;
4305             }
4306             
4307             if(!this.fit_content) {
4308                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4309                 return;
4310             }
4311             
4312             this.setSize(w, Math.min(
4313                 60 +
4314                 this.headerEl.getHeight() + 
4315                 this.footerEl.getHeight() + 
4316                 this.getChildHeight(this.bodyEl.dom.childNodes),
4317                 Roo.lib.Dom.getViewportHeight(true) - 60)
4318             );
4319         }
4320         
4321     },
4322
4323     setSize : function(w,h)
4324     {
4325         if (!w && !h) {
4326             return;
4327         }
4328         
4329         this.resizeTo(w,h);
4330     },
4331
4332     show : function() {
4333
4334         if (!this.rendered) {
4335             this.render();
4336         }
4337         this.toggleHeaderInput(false);
4338         //this.el.setStyle('display', 'block');
4339         this.el.removeClass('hideing');
4340         this.el.dom.style.display='block';
4341         
4342         Roo.get(document.body).addClass('modal-open');
4343  
4344         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4345             
4346             (function(){
4347                 this.el.addClass('show');
4348                 this.el.addClass('in');
4349             }).defer(50, this);
4350         }else{
4351             this.el.addClass('show');
4352             this.el.addClass('in');
4353         }
4354
4355         // not sure how we can show data in here..
4356         //if (this.tmpl) {
4357         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4358         //}
4359
4360         Roo.get(document.body).addClass("x-body-masked");
4361         
4362         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4363         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4364         this.maskEl.dom.style.display = 'block';
4365         this.maskEl.addClass('show');
4366         
4367         
4368         this.resize();
4369         
4370         this.fireEvent('show', this);
4371
4372         // set zindex here - otherwise it appears to be ignored...
4373         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4374
4375         (function () {
4376             this.items.forEach( function(e) {
4377                 e.layout ? e.layout() : false;
4378
4379             });
4380         }).defer(100,this);
4381
4382     },
4383     hide : function()
4384     {
4385         if(this.fireEvent("beforehide", this) !== false){
4386             
4387             this.maskEl.removeClass('show');
4388             
4389             this.maskEl.dom.style.display = '';
4390             Roo.get(document.body).removeClass("x-body-masked");
4391             this.el.removeClass('in');
4392             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4393
4394             if(this.animate){ // why
4395                 this.el.addClass('hideing');
4396                 this.el.removeClass('show');
4397                 (function(){
4398                     if (!this.el.hasClass('hideing')) {
4399                         return; // it's been shown again...
4400                     }
4401                     
4402                     this.el.dom.style.display='';
4403
4404                     Roo.get(document.body).removeClass('modal-open');
4405                     this.el.removeClass('hideing');
4406                 }).defer(150,this);
4407                 
4408             }else{
4409                 this.el.removeClass('show');
4410                 this.el.dom.style.display='';
4411                 Roo.get(document.body).removeClass('modal-open');
4412
4413             }
4414             this.fireEvent('hide', this);
4415         }
4416     },
4417     isVisible : function()
4418     {
4419         
4420         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4421         
4422     },
4423
4424     addButton : function(str, cb)
4425     {
4426
4427
4428         var b = Roo.apply({}, { html : str } );
4429         b.xns = b.xns || Roo.bootstrap;
4430         b.xtype = b.xtype || 'Button';
4431         if (typeof(b.listeners) == 'undefined') {
4432             b.listeners = { click : cb.createDelegate(this)  };
4433         }
4434
4435         var btn = Roo.factory(b);
4436
4437         btn.render(this.getButtonContainer());
4438
4439         return btn;
4440
4441     },
4442
4443     setDefaultButton : function(btn)
4444     {
4445         //this.el.select('.modal-footer').()
4446     },
4447
4448     resizeTo: function(w,h)
4449     {
4450         this.dialogEl.setWidth(w);
4451         
4452         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4453
4454         this.bodyEl.setHeight(h - diff);
4455         
4456         this.fireEvent('resize', this);
4457     },
4458     
4459     setContentSize  : function(w, h)
4460     {
4461
4462     },
4463     onButtonClick: function(btn,e)
4464     {
4465         //Roo.log([a,b,c]);
4466         this.fireEvent('btnclick', btn.name, e);
4467     },
4468      /**
4469      * Set the title of the Dialog
4470      * @param {String} str new Title
4471      */
4472     setTitle: function(str) {
4473         this.titleEl.dom.innerHTML = str;
4474         this.title = str;
4475     },
4476     /**
4477      * Set the body of the Dialog
4478      * @param {String} str new Title
4479      */
4480     setBody: function(str) {
4481         this.bodyEl.dom.innerHTML = str;
4482     },
4483     /**
4484      * Set the body of the Dialog using the template
4485      * @param {Obj} data - apply this data to the template and replace the body contents.
4486      */
4487     applyBody: function(obj)
4488     {
4489         if (!this.tmpl) {
4490             Roo.log("Error - using apply Body without a template");
4491             //code
4492         }
4493         this.tmpl.overwrite(this.bodyEl, obj);
4494     },
4495     
4496     getChildHeight : function(child_nodes)
4497     {
4498         if(
4499             !child_nodes ||
4500             child_nodes.length == 0
4501         ) {
4502             return 0;
4503         }
4504         
4505         var child_height = 0;
4506         
4507         for(var i = 0; i < child_nodes.length; i++) {
4508             
4509             /*
4510             * for modal with tabs...
4511             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4512                 
4513                 var layout_childs = child_nodes[i].childNodes;
4514                 
4515                 for(var j = 0; j < layout_childs.length; j++) {
4516                     
4517                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4518                         
4519                         var layout_body_childs = layout_childs[j].childNodes;
4520                         
4521                         for(var k = 0; k < layout_body_childs.length; k++) {
4522                             
4523                             if(layout_body_childs[k].classList.contains('navbar')) {
4524                                 child_height += layout_body_childs[k].offsetHeight;
4525                                 continue;
4526                             }
4527                             
4528                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4529                                 
4530                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4531                                 
4532                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4533                                     
4534                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4535                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4536                                         continue;
4537                                     }
4538                                     
4539                                 }
4540                                 
4541                             }
4542                             
4543                         }
4544                     }
4545                 }
4546                 continue;
4547             }
4548             */
4549             
4550             child_height += child_nodes[i].offsetHeight;
4551             // Roo.log(child_nodes[i].offsetHeight);
4552         }
4553         
4554         return child_height;
4555     },
4556     toggleHeaderInput : function(is_edit)
4557     {
4558         if (!this.editableTitle) {
4559             return; // not editable.
4560         }
4561         if (is_edit && this.is_header_editing) {
4562             return; // already editing..
4563         }
4564         if (is_edit) {
4565     
4566             this.headerEditEl.dom.value = this.title;
4567             this.headerEditEl.removeClass('d-none');
4568             this.headerEditEl.dom.focus();
4569             this.titleEl.addClass('d-none');
4570             
4571             this.is_header_editing = true;
4572             return
4573         }
4574         // flip back to not editing.
4575         this.title = this.headerEditEl.dom.value;
4576         this.headerEditEl.addClass('d-none');
4577         this.titleEl.removeClass('d-none');
4578         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4579         this.is_header_editing = false;
4580         this.fireEvent('titlechanged', this, this.title);
4581     
4582             
4583         
4584     }
4585
4586 });
4587
4588
4589 Roo.apply(Roo.bootstrap.Modal,  {
4590     /**
4591          * Button config that displays a single OK button
4592          * @type Object
4593          */
4594         OK :  [{
4595             name : 'ok',
4596             weight : 'primary',
4597             html : 'OK'
4598         }],
4599         /**
4600          * Button config that displays Yes and No buttons
4601          * @type Object
4602          */
4603         YESNO : [
4604             {
4605                 name  : 'no',
4606                 html : 'No'
4607             },
4608             {
4609                 name  :'yes',
4610                 weight : 'primary',
4611                 html : 'Yes'
4612             }
4613         ],
4614
4615         /**
4616          * Button config that displays OK and Cancel buttons
4617          * @type Object
4618          */
4619         OKCANCEL : [
4620             {
4621                name : 'cancel',
4622                 html : 'Cancel'
4623             },
4624             {
4625                 name : 'ok',
4626                 weight : 'primary',
4627                 html : 'OK'
4628             }
4629         ],
4630         /**
4631          * Button config that displays Yes, No and Cancel buttons
4632          * @type Object
4633          */
4634         YESNOCANCEL : [
4635             {
4636                 name : 'yes',
4637                 weight : 'primary',
4638                 html : 'Yes'
4639             },
4640             {
4641                 name : 'no',
4642                 html : 'No'
4643             },
4644             {
4645                 name : 'cancel',
4646                 html : 'Cancel'
4647             }
4648         ],
4649         
4650         zIndex : 10001
4651 });
4652
4653 /*
4654  * - LGPL
4655  *
4656  * messagebox - can be used as a replace
4657  * 
4658  */
4659 /**
4660  * @class Roo.MessageBox
4661  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4662  * Example usage:
4663  *<pre><code>
4664 // Basic alert:
4665 Roo.Msg.alert('Status', 'Changes saved successfully.');
4666
4667 // Prompt for user data:
4668 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4669     if (btn == 'ok'){
4670         // process text value...
4671     }
4672 });
4673
4674 // Show a dialog using config options:
4675 Roo.Msg.show({
4676    title:'Save Changes?',
4677    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4678    buttons: Roo.Msg.YESNOCANCEL,
4679    fn: processResult,
4680    animEl: 'elId'
4681 });
4682 </code></pre>
4683  * @singleton
4684  */
4685 Roo.bootstrap.MessageBox = function(){
4686     var dlg, opt, mask, waitTimer;
4687     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4688     var buttons, activeTextEl, bwidth;
4689
4690     
4691     // private
4692     var handleButton = function(button){
4693         dlg.hide();
4694         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4695     };
4696
4697     // private
4698     var handleHide = function(){
4699         if(opt && opt.cls){
4700             dlg.el.removeClass(opt.cls);
4701         }
4702         //if(waitTimer){
4703         //    Roo.TaskMgr.stop(waitTimer);
4704         //    waitTimer = null;
4705         //}
4706     };
4707
4708     // private
4709     var updateButtons = function(b){
4710         var width = 0;
4711         if(!b){
4712             buttons["ok"].hide();
4713             buttons["cancel"].hide();
4714             buttons["yes"].hide();
4715             buttons["no"].hide();
4716             dlg.footerEl.hide();
4717             
4718             return width;
4719         }
4720         dlg.footerEl.show();
4721         for(var k in buttons){
4722             if(typeof buttons[k] != "function"){
4723                 if(b[k]){
4724                     buttons[k].show();
4725                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4726                     width += buttons[k].el.getWidth()+15;
4727                 }else{
4728                     buttons[k].hide();
4729                 }
4730             }
4731         }
4732         return width;
4733     };
4734
4735     // private
4736     var handleEsc = function(d, k, e){
4737         if(opt && opt.closable !== false){
4738             dlg.hide();
4739         }
4740         if(e){
4741             e.stopEvent();
4742         }
4743     };
4744
4745     return {
4746         /**
4747          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4748          * @return {Roo.BasicDialog} The BasicDialog element
4749          */
4750         getDialog : function(){
4751            if(!dlg){
4752                 dlg = new Roo.bootstrap.Modal( {
4753                     //draggable: true,
4754                     //resizable:false,
4755                     //constraintoviewport:false,
4756                     //fixedcenter:true,
4757                     //collapsible : false,
4758                     //shim:true,
4759                     //modal: true,
4760                 //    width: 'auto',
4761                   //  height:100,
4762                     //buttonAlign:"center",
4763                     closeClick : function(){
4764                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4765                             handleButton("no");
4766                         }else{
4767                             handleButton("cancel");
4768                         }
4769                     }
4770                 });
4771                 dlg.render();
4772                 dlg.on("hide", handleHide);
4773                 mask = dlg.mask;
4774                 //dlg.addKeyListener(27, handleEsc);
4775                 buttons = {};
4776                 this.buttons = buttons;
4777                 var bt = this.buttonText;
4778                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4779                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4780                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4781                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4782                 //Roo.log(buttons);
4783                 bodyEl = dlg.bodyEl.createChild({
4784
4785                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4786                         '<textarea class="roo-mb-textarea"></textarea>' +
4787                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4788                 });
4789                 msgEl = bodyEl.dom.firstChild;
4790                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4791                 textboxEl.enableDisplayMode();
4792                 textboxEl.addKeyListener([10,13], function(){
4793                     if(dlg.isVisible() && opt && opt.buttons){
4794                         if(opt.buttons.ok){
4795                             handleButton("ok");
4796                         }else if(opt.buttons.yes){
4797                             handleButton("yes");
4798                         }
4799                     }
4800                 });
4801                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4802                 textareaEl.enableDisplayMode();
4803                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4804                 progressEl.enableDisplayMode();
4805                 
4806                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4807                 var pf = progressEl.dom.firstChild;
4808                 if (pf) {
4809                     pp = Roo.get(pf.firstChild);
4810                     pp.setHeight(pf.offsetHeight);
4811                 }
4812                 
4813             }
4814             return dlg;
4815         },
4816
4817         /**
4818          * Updates the message box body text
4819          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4820          * the XHTML-compliant non-breaking space character '&amp;#160;')
4821          * @return {Roo.MessageBox} This message box
4822          */
4823         updateText : function(text)
4824         {
4825             if(!dlg.isVisible() && !opt.width){
4826                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4827                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4828             }
4829             msgEl.innerHTML = text || '&#160;';
4830       
4831             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4832             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4833             var w = Math.max(
4834                     Math.min(opt.width || cw , this.maxWidth), 
4835                     Math.max(opt.minWidth || this.minWidth, bwidth)
4836             );
4837             if(opt.prompt){
4838                 activeTextEl.setWidth(w);
4839             }
4840             if(dlg.isVisible()){
4841                 dlg.fixedcenter = false;
4842             }
4843             // to big, make it scroll. = But as usual stupid IE does not support
4844             // !important..
4845             
4846             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4847                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4848                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4849             } else {
4850                 bodyEl.dom.style.height = '';
4851                 bodyEl.dom.style.overflowY = '';
4852             }
4853             if (cw > w) {
4854                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4855             } else {
4856                 bodyEl.dom.style.overflowX = '';
4857             }
4858             
4859             dlg.setContentSize(w, bodyEl.getHeight());
4860             if(dlg.isVisible()){
4861                 dlg.fixedcenter = true;
4862             }
4863             return this;
4864         },
4865
4866         /**
4867          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4868          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4869          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4870          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4871          * @return {Roo.MessageBox} This message box
4872          */
4873         updateProgress : function(value, text){
4874             if(text){
4875                 this.updateText(text);
4876             }
4877             
4878             if (pp) { // weird bug on my firefox - for some reason this is not defined
4879                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4880                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4881             }
4882             return this;
4883         },        
4884
4885         /**
4886          * Returns true if the message box is currently displayed
4887          * @return {Boolean} True if the message box is visible, else false
4888          */
4889         isVisible : function(){
4890             return dlg && dlg.isVisible();  
4891         },
4892
4893         /**
4894          * Hides the message box if it is displayed
4895          */
4896         hide : function(){
4897             if(this.isVisible()){
4898                 dlg.hide();
4899             }  
4900         },
4901
4902         /**
4903          * Displays a new message box, or reinitializes an existing message box, based on the config options
4904          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4905          * The following config object properties are supported:
4906          * <pre>
4907 Property    Type             Description
4908 ----------  ---------------  ------------------------------------------------------------------------------------
4909 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4910                                    closes (defaults to undefined)
4911 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4912                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4913 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4914                                    progress and wait dialogs will ignore this property and always hide the
4915                                    close button as they can only be closed programmatically.
4916 cls               String           A custom CSS class to apply to the message box element
4917 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4918                                    displayed (defaults to 75)
4919 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4920                                    function will be btn (the name of the button that was clicked, if applicable,
4921                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4922                                    Progress and wait dialogs will ignore this option since they do not respond to
4923                                    user actions and can only be closed programmatically, so any required function
4924                                    should be called by the same code after it closes the dialog.
4925 icon              String           A CSS class that provides a background image to be used as an icon for
4926                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4927 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4928 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4929 modal             Boolean          False to allow user interaction with the page while the message box is
4930                                    displayed (defaults to true)
4931 msg               String           A string that will replace the existing message box body text (defaults
4932                                    to the XHTML-compliant non-breaking space character '&#160;')
4933 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4934 progress          Boolean          True to display a progress bar (defaults to false)
4935 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4936 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4937 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4938 title             String           The title text
4939 value             String           The string value to set into the active textbox element if displayed
4940 wait              Boolean          True to display a progress bar (defaults to false)
4941 width             Number           The width of the dialog in pixels
4942 </pre>
4943          *
4944          * Example usage:
4945          * <pre><code>
4946 Roo.Msg.show({
4947    title: 'Address',
4948    msg: 'Please enter your address:',
4949    width: 300,
4950    buttons: Roo.MessageBox.OKCANCEL,
4951    multiline: true,
4952    fn: saveAddress,
4953    animEl: 'addAddressBtn'
4954 });
4955 </code></pre>
4956          * @param {Object} config Configuration options
4957          * @return {Roo.MessageBox} This message box
4958          */
4959         show : function(options)
4960         {
4961             
4962             // this causes nightmares if you show one dialog after another
4963             // especially on callbacks..
4964              
4965             if(this.isVisible()){
4966                 
4967                 this.hide();
4968                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4969                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4970                 Roo.log("New Dialog Message:" +  options.msg )
4971                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4972                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4973                 
4974             }
4975             var d = this.getDialog();
4976             opt = options;
4977             d.setTitle(opt.title || "&#160;");
4978             d.closeEl.setDisplayed(opt.closable !== false);
4979             activeTextEl = textboxEl;
4980             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4981             if(opt.prompt){
4982                 if(opt.multiline){
4983                     textboxEl.hide();
4984                     textareaEl.show();
4985                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4986                         opt.multiline : this.defaultTextHeight);
4987                     activeTextEl = textareaEl;
4988                 }else{
4989                     textboxEl.show();
4990                     textareaEl.hide();
4991                 }
4992             }else{
4993                 textboxEl.hide();
4994                 textareaEl.hide();
4995             }
4996             progressEl.setDisplayed(opt.progress === true);
4997             if (opt.progress) {
4998                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4999             }
5000             this.updateProgress(0);
5001             activeTextEl.dom.value = opt.value || "";
5002             if(opt.prompt){
5003                 dlg.setDefaultButton(activeTextEl);
5004             }else{
5005                 var bs = opt.buttons;
5006                 var db = null;
5007                 if(bs && bs.ok){
5008                     db = buttons["ok"];
5009                 }else if(bs && bs.yes){
5010                     db = buttons["yes"];
5011                 }
5012                 dlg.setDefaultButton(db);
5013             }
5014             bwidth = updateButtons(opt.buttons);
5015             this.updateText(opt.msg);
5016             if(opt.cls){
5017                 d.el.addClass(opt.cls);
5018             }
5019             d.proxyDrag = opt.proxyDrag === true;
5020             d.modal = opt.modal !== false;
5021             d.mask = opt.modal !== false ? mask : false;
5022             if(!d.isVisible()){
5023                 // force it to the end of the z-index stack so it gets a cursor in FF
5024                 document.body.appendChild(dlg.el.dom);
5025                 d.animateTarget = null;
5026                 d.show(options.animEl);
5027             }
5028             return this;
5029         },
5030
5031         /**
5032          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5033          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5034          * and closing the message box when the process is complete.
5035          * @param {String} title The title bar text
5036          * @param {String} msg The message box body text
5037          * @return {Roo.MessageBox} This message box
5038          */
5039         progress : function(title, msg){
5040             this.show({
5041                 title : title,
5042                 msg : msg,
5043                 buttons: false,
5044                 progress:true,
5045                 closable:false,
5046                 minWidth: this.minProgressWidth,
5047                 modal : true
5048             });
5049             return this;
5050         },
5051
5052         /**
5053          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5054          * If a callback function is passed it will be called after the user clicks the button, and the
5055          * id of the button that was clicked will be passed as the only parameter to the callback
5056          * (could also be the top-right close button).
5057          * @param {String} title The title bar text
5058          * @param {String} msg The message box body text
5059          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5060          * @param {Object} scope (optional) The scope of the callback function
5061          * @return {Roo.MessageBox} This message box
5062          */
5063         alert : function(title, msg, fn, scope)
5064         {
5065             this.show({
5066                 title : title,
5067                 msg : msg,
5068                 buttons: this.OK,
5069                 fn: fn,
5070                 closable : false,
5071                 scope : scope,
5072                 modal : true
5073             });
5074             return this;
5075         },
5076
5077         /**
5078          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5079          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5080          * You are responsible for closing the message box when the process is complete.
5081          * @param {String} msg The message box body text
5082          * @param {String} title (optional) The title bar text
5083          * @return {Roo.MessageBox} This message box
5084          */
5085         wait : function(msg, title){
5086             this.show({
5087                 title : title,
5088                 msg : msg,
5089                 buttons: false,
5090                 closable:false,
5091                 progress:true,
5092                 modal:true,
5093                 width:300,
5094                 wait:true
5095             });
5096             waitTimer = Roo.TaskMgr.start({
5097                 run: function(i){
5098                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5099                 },
5100                 interval: 1000
5101             });
5102             return this;
5103         },
5104
5105         /**
5106          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5107          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5108          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5109          * @param {String} title The title bar text
5110          * @param {String} msg The message box body text
5111          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5112          * @param {Object} scope (optional) The scope of the callback function
5113          * @return {Roo.MessageBox} This message box
5114          */
5115         confirm : function(title, msg, fn, scope){
5116             this.show({
5117                 title : title,
5118                 msg : msg,
5119                 buttons: this.YESNO,
5120                 fn: fn,
5121                 scope : scope,
5122                 modal : true
5123             });
5124             return this;
5125         },
5126
5127         /**
5128          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5129          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5130          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5131          * (could also be the top-right close button) and the text that was entered will be passed as the two
5132          * parameters to the callback.
5133          * @param {String} title The title bar text
5134          * @param {String} msg The message box body text
5135          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5136          * @param {Object} scope (optional) The scope of the callback function
5137          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5138          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5139          * @return {Roo.MessageBox} This message box
5140          */
5141         prompt : function(title, msg, fn, scope, multiline){
5142             this.show({
5143                 title : title,
5144                 msg : msg,
5145                 buttons: this.OKCANCEL,
5146                 fn: fn,
5147                 minWidth:250,
5148                 scope : scope,
5149                 prompt:true,
5150                 multiline: multiline,
5151                 modal : true
5152             });
5153             return this;
5154         },
5155
5156         /**
5157          * Button config that displays a single OK button
5158          * @type Object
5159          */
5160         OK : {ok:true},
5161         /**
5162          * Button config that displays Yes and No buttons
5163          * @type Object
5164          */
5165         YESNO : {yes:true, no:true},
5166         /**
5167          * Button config that displays OK and Cancel buttons
5168          * @type Object
5169          */
5170         OKCANCEL : {ok:true, cancel:true},
5171         /**
5172          * Button config that displays Yes, No and Cancel buttons
5173          * @type Object
5174          */
5175         YESNOCANCEL : {yes:true, no:true, cancel:true},
5176
5177         /**
5178          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5179          * @type Number
5180          */
5181         defaultTextHeight : 75,
5182         /**
5183          * The maximum width in pixels of the message box (defaults to 600)
5184          * @type Number
5185          */
5186         maxWidth : 600,
5187         /**
5188          * The minimum width in pixels of the message box (defaults to 100)
5189          * @type Number
5190          */
5191         minWidth : 100,
5192         /**
5193          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5194          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5195          * @type Number
5196          */
5197         minProgressWidth : 250,
5198         /**
5199          * An object containing the default button text strings that can be overriden for localized language support.
5200          * Supported properties are: ok, cancel, yes and no.
5201          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5202          * @type Object
5203          */
5204         buttonText : {
5205             ok : "OK",
5206             cancel : "Cancel",
5207             yes : "Yes",
5208             no : "No"
5209         }
5210     };
5211 }();
5212
5213 /**
5214  * Shorthand for {@link Roo.MessageBox}
5215  */
5216 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5217 Roo.Msg = Roo.Msg || Roo.MessageBox;
5218 /*
5219  * - LGPL
5220  *
5221  * navbar
5222  * 
5223  */
5224
5225 /**
5226  * @class Roo.bootstrap.Navbar
5227  * @extends Roo.bootstrap.Component
5228  * Bootstrap Navbar class
5229
5230  * @constructor
5231  * Create a new Navbar
5232  * @param {Object} config The config object
5233  */
5234
5235
5236 Roo.bootstrap.Navbar = function(config){
5237     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5238     this.addEvents({
5239         // raw events
5240         /**
5241          * @event beforetoggle
5242          * Fire before toggle the menu
5243          * @param {Roo.EventObject} e
5244          */
5245         "beforetoggle" : true
5246     });
5247 };
5248
5249 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5250     
5251     
5252    
5253     // private
5254     navItems : false,
5255     loadMask : false,
5256     
5257     
5258     getAutoCreate : function(){
5259         
5260         
5261         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5262         
5263     },
5264     
5265     initEvents :function ()
5266     {
5267         //Roo.log(this.el.select('.navbar-toggle',true));
5268         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5269         
5270         var mark = {
5271             tag: "div",
5272             cls:"x-dlg-mask"
5273         };
5274         
5275         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5276         
5277         var size = this.el.getSize();
5278         this.maskEl.setSize(size.width, size.height);
5279         this.maskEl.enableDisplayMode("block");
5280         this.maskEl.hide();
5281         
5282         if(this.loadMask){
5283             this.maskEl.show();
5284         }
5285     },
5286     
5287     
5288     getChildContainer : function()
5289     {
5290         if (this.el && this.el.select('.collapse').getCount()) {
5291             return this.el.select('.collapse',true).first();
5292         }
5293         
5294         return this.el;
5295     },
5296     
5297     mask : function()
5298     {
5299         this.maskEl.show();
5300     },
5301     
5302     unmask : function()
5303     {
5304         this.maskEl.hide();
5305     },
5306     onToggle : function()
5307     {
5308         
5309         if(this.fireEvent('beforetoggle', this) === false){
5310             return;
5311         }
5312         var ce = this.el.select('.navbar-collapse',true).first();
5313       
5314         if (!ce.hasClass('show')) {
5315            this.expand();
5316         } else {
5317             this.collapse();
5318         }
5319         
5320         
5321     
5322     },
5323     /**
5324      * Expand the navbar pulldown 
5325      */
5326     expand : function ()
5327     {
5328        
5329         var ce = this.el.select('.navbar-collapse',true).first();
5330         if (ce.hasClass('collapsing')) {
5331             return;
5332         }
5333         ce.dom.style.height = '';
5334                // show it...
5335         ce.addClass('in'); // old...
5336         ce.removeClass('collapse');
5337         ce.addClass('show');
5338         var h = ce.getHeight();
5339         Roo.log(h);
5340         ce.removeClass('show');
5341         // at this point we should be able to see it..
5342         ce.addClass('collapsing');
5343         
5344         ce.setHeight(0); // resize it ...
5345         ce.on('transitionend', function() {
5346             //Roo.log('done transition');
5347             ce.removeClass('collapsing');
5348             ce.addClass('show');
5349             ce.removeClass('collapse');
5350
5351             ce.dom.style.height = '';
5352         }, this, { single: true} );
5353         ce.setHeight(h);
5354         ce.dom.scrollTop = 0;
5355     },
5356     /**
5357      * Collapse the navbar pulldown 
5358      */
5359     collapse : function()
5360     {
5361          var ce = this.el.select('.navbar-collapse',true).first();
5362        
5363         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5364             // it's collapsed or collapsing..
5365             return;
5366         }
5367         ce.removeClass('in'); // old...
5368         ce.setHeight(ce.getHeight());
5369         ce.removeClass('show');
5370         ce.addClass('collapsing');
5371         
5372         ce.on('transitionend', function() {
5373             ce.dom.style.height = '';
5374             ce.removeClass('collapsing');
5375             ce.addClass('collapse');
5376         }, this, { single: true} );
5377         ce.setHeight(0);
5378     }
5379     
5380     
5381     
5382 });
5383
5384
5385
5386  
5387
5388  /*
5389  * - LGPL
5390  *
5391  * navbar
5392  * 
5393  */
5394
5395 /**
5396  * @class Roo.bootstrap.NavSimplebar
5397  * @extends Roo.bootstrap.Navbar
5398  * Bootstrap Sidebar class
5399  *
5400  * @cfg {Boolean} inverse is inverted color
5401  * 
5402  * @cfg {String} type (nav | pills | tabs)
5403  * @cfg {Boolean} arrangement stacked | justified
5404  * @cfg {String} align (left | right) alignment
5405  * 
5406  * @cfg {Boolean} main (true|false) main nav bar? default false
5407  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5408  * 
5409  * @cfg {String} tag (header|footer|nav|div) default is nav 
5410
5411  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5412  * 
5413  * 
5414  * @constructor
5415  * Create a new Sidebar
5416  * @param {Object} config The config object
5417  */
5418
5419
5420 Roo.bootstrap.NavSimplebar = function(config){
5421     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5422 };
5423
5424 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5425     
5426     inverse: false,
5427     
5428     type: false,
5429     arrangement: '',
5430     align : false,
5431     
5432     weight : 'light',
5433     
5434     main : false,
5435     
5436     
5437     tag : false,
5438     
5439     
5440     getAutoCreate : function(){
5441         
5442         
5443         var cfg = {
5444             tag : this.tag || 'div',
5445             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5446         };
5447         if (['light','white'].indexOf(this.weight) > -1) {
5448             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5449         }
5450         cfg.cls += ' bg-' + this.weight;
5451         
5452         if (this.inverse) {
5453             cfg.cls += ' navbar-inverse';
5454             
5455         }
5456         
5457         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5458         
5459         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5460             return cfg;
5461         }
5462         
5463         
5464     
5465         
5466         cfg.cn = [
5467             {
5468                 cls: 'nav nav-' + this.xtype,
5469                 tag : 'ul'
5470             }
5471         ];
5472         
5473          
5474         this.type = this.type || 'nav';
5475         if (['tabs','pills'].indexOf(this.type) != -1) {
5476             cfg.cn[0].cls += ' nav-' + this.type
5477         
5478         
5479         } else {
5480             if (this.type!=='nav') {
5481                 Roo.log('nav type must be nav/tabs/pills')
5482             }
5483             cfg.cn[0].cls += ' navbar-nav'
5484         }
5485         
5486         
5487         
5488         
5489         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5490             cfg.cn[0].cls += ' nav-' + this.arrangement;
5491         }
5492         
5493         
5494         if (this.align === 'right') {
5495             cfg.cn[0].cls += ' navbar-right';
5496         }
5497         
5498         
5499         
5500         
5501         return cfg;
5502     
5503         
5504     }
5505     
5506     
5507     
5508 });
5509
5510
5511
5512  
5513
5514  
5515        /*
5516  * - LGPL
5517  *
5518  * navbar
5519  * navbar-fixed-top
5520  * navbar-expand-md  fixed-top 
5521  */
5522
5523 /**
5524  * @class Roo.bootstrap.NavHeaderbar
5525  * @extends Roo.bootstrap.NavSimplebar
5526  * Bootstrap Sidebar class
5527  *
5528  * @cfg {String} brand what is brand
5529  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5530  * @cfg {String} brand_href href of the brand
5531  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5532  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5533  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5534  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5535  * 
5536  * @constructor
5537  * Create a new Sidebar
5538  * @param {Object} config The config object
5539  */
5540
5541
5542 Roo.bootstrap.NavHeaderbar = function(config){
5543     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5544       
5545 };
5546
5547 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5548     
5549     position: '',
5550     brand: '',
5551     brand_href: false,
5552     srButton : true,
5553     autohide : false,
5554     desktopCenter : false,
5555    
5556     
5557     getAutoCreate : function(){
5558         
5559         var   cfg = {
5560             tag: this.nav || 'nav',
5561             cls: 'navbar navbar-expand-md',
5562             role: 'navigation',
5563             cn: []
5564         };
5565         
5566         var cn = cfg.cn;
5567         if (this.desktopCenter) {
5568             cn.push({cls : 'container', cn : []});
5569             cn = cn[0].cn;
5570         }
5571         
5572         if(this.srButton){
5573             var btn = {
5574                 tag: 'button',
5575                 type: 'button',
5576                 cls: 'navbar-toggle navbar-toggler',
5577                 'data-toggle': 'collapse',
5578                 cn: [
5579                     {
5580                         tag: 'span',
5581                         cls: 'sr-only',
5582                         html: 'Toggle navigation'
5583                     },
5584                     {
5585                         tag: 'span',
5586                         cls: 'icon-bar navbar-toggler-icon'
5587                     },
5588                     {
5589                         tag: 'span',
5590                         cls: 'icon-bar'
5591                     },
5592                     {
5593                         tag: 'span',
5594                         cls: 'icon-bar'
5595                     }
5596                 ]
5597             };
5598             
5599             cn.push( Roo.bootstrap.version == 4 ? btn : {
5600                 tag: 'div',
5601                 cls: 'navbar-header',
5602                 cn: [
5603                     btn
5604                 ]
5605             });
5606         }
5607         
5608         cn.push({
5609             tag: 'div',
5610             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5611             cn : []
5612         });
5613         
5614         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5615         
5616         if (['light','white'].indexOf(this.weight) > -1) {
5617             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5618         }
5619         cfg.cls += ' bg-' + this.weight;
5620         
5621         
5622         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5623             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5624             
5625             // tag can override this..
5626             
5627             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5628         }
5629         
5630         if (this.brand !== '') {
5631             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5632             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5633                 tag: 'a',
5634                 href: this.brand_href ? this.brand_href : '#',
5635                 cls: 'navbar-brand',
5636                 cn: [
5637                 this.brand
5638                 ]
5639             });
5640         }
5641         
5642         if(this.main){
5643             cfg.cls += ' main-nav';
5644         }
5645         
5646         
5647         return cfg;
5648
5649         
5650     },
5651     getHeaderChildContainer : function()
5652     {
5653         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5654             return this.el.select('.navbar-header',true).first();
5655         }
5656         
5657         return this.getChildContainer();
5658     },
5659     
5660     getChildContainer : function()
5661     {
5662          
5663         return this.el.select('.roo-navbar-collapse',true).first();
5664          
5665         
5666     },
5667     
5668     initEvents : function()
5669     {
5670         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5671         
5672         if (this.autohide) {
5673             
5674             var prevScroll = 0;
5675             var ft = this.el;
5676             
5677             Roo.get(document).on('scroll',function(e) {
5678                 var ns = Roo.get(document).getScroll().top;
5679                 var os = prevScroll;
5680                 prevScroll = ns;
5681                 
5682                 if(ns > os){
5683                     ft.removeClass('slideDown');
5684                     ft.addClass('slideUp');
5685                     return;
5686                 }
5687                 ft.removeClass('slideUp');
5688                 ft.addClass('slideDown');
5689                  
5690               
5691           },this);
5692         }
5693     }    
5694     
5695 });
5696
5697
5698
5699  
5700
5701  /*
5702  * - LGPL
5703  *
5704  * navbar
5705  * 
5706  */
5707
5708 /**
5709  * @class Roo.bootstrap.NavSidebar
5710  * @extends Roo.bootstrap.Navbar
5711  * Bootstrap Sidebar class
5712  * 
5713  * @constructor
5714  * Create a new Sidebar
5715  * @param {Object} config The config object
5716  */
5717
5718
5719 Roo.bootstrap.NavSidebar = function(config){
5720     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5721 };
5722
5723 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5724     
5725     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5726     
5727     getAutoCreate : function(){
5728         
5729         
5730         return  {
5731             tag: 'div',
5732             cls: 'sidebar sidebar-nav'
5733         };
5734     
5735         
5736     }
5737     
5738     
5739     
5740 });
5741
5742
5743
5744  
5745
5746  /*
5747  * - LGPL
5748  *
5749  * nav group
5750  * 
5751  */
5752
5753 /**
5754  * @class Roo.bootstrap.NavGroup
5755  * @extends Roo.bootstrap.Component
5756  * Bootstrap NavGroup class
5757  * @cfg {String} align (left|right)
5758  * @cfg {Boolean} inverse
5759  * @cfg {String} type (nav|pills|tab) default nav
5760  * @cfg {String} navId - reference Id for navbar.
5761  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5762  * 
5763  * @constructor
5764  * Create a new nav group
5765  * @param {Object} config The config object
5766  */
5767
5768 Roo.bootstrap.NavGroup = function(config){
5769     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5770     this.navItems = [];
5771    
5772     Roo.bootstrap.NavGroup.register(this);
5773      this.addEvents({
5774         /**
5775              * @event changed
5776              * Fires when the active item changes
5777              * @param {Roo.bootstrap.NavGroup} this
5778              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5779              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5780          */
5781         'changed': true
5782      });
5783     
5784 };
5785
5786 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5787     
5788     align: '',
5789     inverse: false,
5790     form: false,
5791     type: 'nav',
5792     navId : '',
5793     // private
5794     pilltype : true,
5795     
5796     navItems : false, 
5797     
5798     getAutoCreate : function()
5799     {
5800         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5801         
5802         cfg = {
5803             tag : 'ul',
5804             cls: 'nav' 
5805         };
5806         if (Roo.bootstrap.version == 4) {
5807             if (['tabs','pills'].indexOf(this.type) != -1) {
5808                 cfg.cls += ' nav-' + this.type; 
5809             } else {
5810                 // trying to remove so header bar can right align top?
5811                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5812                     // do not use on header bar... 
5813                     cfg.cls += ' navbar-nav';
5814                 }
5815             }
5816             
5817         } else {
5818             if (['tabs','pills'].indexOf(this.type) != -1) {
5819                 cfg.cls += ' nav-' + this.type
5820             } else {
5821                 if (this.type !== 'nav') {
5822                     Roo.log('nav type must be nav/tabs/pills')
5823                 }
5824                 cfg.cls += ' navbar-nav'
5825             }
5826         }
5827         
5828         if (this.parent() && this.parent().sidebar) {
5829             cfg = {
5830                 tag: 'ul',
5831                 cls: 'dashboard-menu sidebar-menu'
5832             };
5833             
5834             return cfg;
5835         }
5836         
5837         if (this.form === true) {
5838             cfg = {
5839                 tag: 'form',
5840                 cls: 'navbar-form form-inline'
5841             };
5842             //nav navbar-right ml-md-auto
5843             if (this.align === 'right') {
5844                 cfg.cls += ' navbar-right ml-md-auto';
5845             } else {
5846                 cfg.cls += ' navbar-left';
5847             }
5848         }
5849         
5850         if (this.align === 'right') {
5851             cfg.cls += ' navbar-right ml-md-auto';
5852         } else {
5853             cfg.cls += ' mr-auto';
5854         }
5855         
5856         if (this.inverse) {
5857             cfg.cls += ' navbar-inverse';
5858             
5859         }
5860         
5861         
5862         return cfg;
5863     },
5864     /**
5865     * sets the active Navigation item
5866     * @param {Roo.bootstrap.NavItem} the new current navitem
5867     */
5868     setActiveItem : function(item)
5869     {
5870         var prev = false;
5871         Roo.each(this.navItems, function(v){
5872             if (v == item) {
5873                 return ;
5874             }
5875             if (v.isActive()) {
5876                 v.setActive(false, true);
5877                 prev = v;
5878                 
5879             }
5880             
5881         });
5882
5883         item.setActive(true, true);
5884         this.fireEvent('changed', this, item, prev);
5885         
5886         
5887     },
5888     /**
5889     * gets the active Navigation item
5890     * @return {Roo.bootstrap.NavItem} the current navitem
5891     */
5892     getActive : function()
5893     {
5894         
5895         var prev = false;
5896         Roo.each(this.navItems, function(v){
5897             
5898             if (v.isActive()) {
5899                 prev = v;
5900                 
5901             }
5902             
5903         });
5904         return prev;
5905     },
5906     
5907     indexOfNav : function()
5908     {
5909         
5910         var prev = false;
5911         Roo.each(this.navItems, function(v,i){
5912             
5913             if (v.isActive()) {
5914                 prev = i;
5915                 
5916             }
5917             
5918         });
5919         return prev;
5920     },
5921     /**
5922     * adds a Navigation item
5923     * @param {Roo.bootstrap.NavItem} the navitem to add
5924     */
5925     addItem : function(cfg)
5926     {
5927         if (this.form && Roo.bootstrap.version == 4) {
5928             cfg.tag = 'div';
5929         }
5930         var cn = new Roo.bootstrap.NavItem(cfg);
5931         this.register(cn);
5932         cn.parentId = this.id;
5933         cn.onRender(this.el, null);
5934         return cn;
5935     },
5936     /**
5937     * register a Navigation item
5938     * @param {Roo.bootstrap.NavItem} the navitem to add
5939     */
5940     register : function(item)
5941     {
5942         this.navItems.push( item);
5943         item.navId = this.navId;
5944     
5945     },
5946     
5947     /**
5948     * clear all the Navigation item
5949     */
5950    
5951     clearAll : function()
5952     {
5953         this.navItems = [];
5954         this.el.dom.innerHTML = '';
5955     },
5956     
5957     getNavItem: function(tabId)
5958     {
5959         var ret = false;
5960         Roo.each(this.navItems, function(e) {
5961             if (e.tabId == tabId) {
5962                ret =  e;
5963                return false;
5964             }
5965             return true;
5966             
5967         });
5968         return ret;
5969     },
5970     
5971     setActiveNext : function()
5972     {
5973         var i = this.indexOfNav(this.getActive());
5974         if (i > this.navItems.length) {
5975             return;
5976         }
5977         this.setActiveItem(this.navItems[i+1]);
5978     },
5979     setActivePrev : function()
5980     {
5981         var i = this.indexOfNav(this.getActive());
5982         if (i  < 1) {
5983             return;
5984         }
5985         this.setActiveItem(this.navItems[i-1]);
5986     },
5987     clearWasActive : function(except) {
5988         Roo.each(this.navItems, function(e) {
5989             if (e.tabId != except.tabId && e.was_active) {
5990                e.was_active = false;
5991                return false;
5992             }
5993             return true;
5994             
5995         });
5996     },
5997     getWasActive : function ()
5998     {
5999         var r = false;
6000         Roo.each(this.navItems, function(e) {
6001             if (e.was_active) {
6002                r = e;
6003                return false;
6004             }
6005             return true;
6006             
6007         });
6008         return r;
6009     }
6010     
6011     
6012 });
6013
6014  
6015 Roo.apply(Roo.bootstrap.NavGroup, {
6016     
6017     groups: {},
6018      /**
6019     * register a Navigation Group
6020     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6021     */
6022     register : function(navgrp)
6023     {
6024         this.groups[navgrp.navId] = navgrp;
6025         
6026     },
6027     /**
6028     * fetch a Navigation Group based on the navigation ID
6029     * @param {string} the navgroup to add
6030     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6031     */
6032     get: function(navId) {
6033         if (typeof(this.groups[navId]) == 'undefined') {
6034             return false;
6035             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6036         }
6037         return this.groups[navId] ;
6038     }
6039     
6040     
6041     
6042 });
6043
6044  /*
6045  * - LGPL
6046  *
6047  * row
6048  * 
6049  */
6050
6051 /**
6052  * @class Roo.bootstrap.NavItem
6053  * @extends Roo.bootstrap.Component
6054  * Bootstrap Navbar.NavItem class
6055  * @cfg {String} href  link to
6056  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6057  * @cfg {Boolean} button_outline show and outlined button
6058  * @cfg {String} html content of button
6059  * @cfg {String} badge text inside badge
6060  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6061  * @cfg {String} glyphicon DEPRICATED - use fa
6062  * @cfg {String} icon DEPRICATED - use fa
6063  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6064  * @cfg {Boolean} active Is item active
6065  * @cfg {Boolean} disabled Is item disabled
6066  * @cfg {String} linkcls  Link Class
6067  * @cfg {Boolean} preventDefault (true | false) default false
6068  * @cfg {String} tabId the tab that this item activates.
6069  * @cfg {String} tagtype (a|span) render as a href or span?
6070  * @cfg {Boolean} animateRef (true|false) link to element default false  
6071   
6072  * @constructor
6073  * Create a new Navbar Item
6074  * @param {Object} config The config object
6075  */
6076 Roo.bootstrap.NavItem = function(config){
6077     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6078     this.addEvents({
6079         // raw events
6080         /**
6081          * @event click
6082          * The raw click event for the entire grid.
6083          * @param {Roo.EventObject} e
6084          */
6085         "click" : true,
6086          /**
6087             * @event changed
6088             * Fires when the active item active state changes
6089             * @param {Roo.bootstrap.NavItem} this
6090             * @param {boolean} state the new state
6091              
6092          */
6093         'changed': true,
6094         /**
6095             * @event scrollto
6096             * Fires when scroll to element
6097             * @param {Roo.bootstrap.NavItem} this
6098             * @param {Object} options
6099             * @param {Roo.EventObject} e
6100              
6101          */
6102         'scrollto': true
6103     });
6104    
6105 };
6106
6107 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6108     
6109     href: false,
6110     html: '',
6111     badge: '',
6112     icon: false,
6113     fa : false,
6114     glyphicon: false,
6115     active: false,
6116     preventDefault : false,
6117     tabId : false,
6118     tagtype : 'a',
6119     tag: 'li',
6120     disabled : false,
6121     animateRef : false,
6122     was_active : false,
6123     button_weight : '',
6124     button_outline : false,
6125     linkcls : '',
6126     navLink: false,
6127     
6128     getAutoCreate : function(){
6129          
6130         var cfg = {
6131             tag: this.tag,
6132             cls: 'nav-item'
6133         };
6134         
6135         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6136         
6137         if (this.active) {
6138             cfg.cls +=  ' active' ;
6139         }
6140         if (this.disabled) {
6141             cfg.cls += ' disabled';
6142         }
6143         
6144         // BS4 only?
6145         if (this.button_weight.length) {
6146             cfg.tag = this.href ? 'a' : 'button';
6147             cfg.html = this.html || '';
6148             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6149             if (this.href) {
6150                 cfg.href = this.href;
6151             }
6152             if (this.fa) {
6153                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6154             }
6155             
6156             // menu .. should add dropdown-menu class - so no need for carat..
6157             
6158             if (this.badge !== '') {
6159                  
6160                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6161             }
6162             return cfg;
6163         }
6164         
6165         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6166             cfg.cn = [
6167                 {
6168                     tag: this.tagtype,
6169                     href : this.href || "#",
6170                     html: this.html || ''
6171                 }
6172             ];
6173             if (this.tagtype == 'a') {
6174                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6175         
6176             }
6177             if (this.icon) {
6178                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6179             }
6180             if (this.fa) {
6181                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6182             }
6183             if(this.glyphicon) {
6184                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6185             }
6186             
6187             if (this.menu) {
6188                 
6189                 cfg.cn[0].html += " <span class='caret'></span>";
6190              
6191             }
6192             
6193             if (this.badge !== '') {
6194                  
6195                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6196             }
6197         }
6198         
6199         
6200         
6201         return cfg;
6202     },
6203     onRender : function(ct, position)
6204     {
6205        // Roo.log("Call onRender: " + this.xtype);
6206         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6207             this.tag = 'div';
6208         }
6209         
6210         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6211         this.navLink = this.el.select('.nav-link',true).first();
6212         return ret;
6213     },
6214       
6215     
6216     initEvents: function() 
6217     {
6218         if (typeof (this.menu) != 'undefined') {
6219             this.menu.parentType = this.xtype;
6220             this.menu.triggerEl = this.el;
6221             this.menu = this.addxtype(Roo.apply({}, this.menu));
6222         }
6223         
6224         this.el.on('click', this.onClick, this);
6225         
6226         //if(this.tagtype == 'span'){
6227         //    this.el.select('span',true).on('click', this.onClick, this);
6228         //}
6229        
6230         // at this point parent should be available..
6231         this.parent().register(this);
6232     },
6233     
6234     onClick : function(e)
6235     {
6236         if (e.getTarget('.dropdown-menu-item')) {
6237             // did you click on a menu itemm.... - then don't trigger onclick..
6238             return;
6239         }
6240         
6241         if(
6242                 this.preventDefault || 
6243                 this.href == '#' 
6244         ){
6245             Roo.log("NavItem - prevent Default?");
6246             e.preventDefault();
6247         }
6248         
6249         if (this.disabled) {
6250             return;
6251         }
6252         
6253         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6254         if (tg && tg.transition) {
6255             Roo.log("waiting for the transitionend");
6256             return;
6257         }
6258         
6259         
6260         
6261         //Roo.log("fire event clicked");
6262         if(this.fireEvent('click', this, e) === false){
6263             return;
6264         };
6265         
6266         if(this.tagtype == 'span'){
6267             return;
6268         }
6269         
6270         //Roo.log(this.href);
6271         var ael = this.el.select('a',true).first();
6272         //Roo.log(ael);
6273         
6274         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6275             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6276             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6277                 return; // ignore... - it's a 'hash' to another page.
6278             }
6279             Roo.log("NavItem - prevent Default?");
6280             e.preventDefault();
6281             this.scrollToElement(e);
6282         }
6283         
6284         
6285         var p =  this.parent();
6286    
6287         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6288             if (typeof(p.setActiveItem) !== 'undefined') {
6289                 p.setActiveItem(this);
6290             }
6291         }
6292         
6293         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6294         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6295             // remove the collapsed menu expand...
6296             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6297         }
6298     },
6299     
6300     isActive: function () {
6301         return this.active
6302     },
6303     setActive : function(state, fire, is_was_active)
6304     {
6305         if (this.active && !state && this.navId) {
6306             this.was_active = true;
6307             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6308             if (nv) {
6309                 nv.clearWasActive(this);
6310             }
6311             
6312         }
6313         this.active = state;
6314         
6315         if (!state ) {
6316             this.el.removeClass('active');
6317             this.navLink ? this.navLink.removeClass('active') : false;
6318         } else if (!this.el.hasClass('active')) {
6319             
6320             this.el.addClass('active');
6321             if (Roo.bootstrap.version == 4 && this.navLink ) {
6322                 this.navLink.addClass('active');
6323             }
6324             
6325         }
6326         if (fire) {
6327             this.fireEvent('changed', this, state);
6328         }
6329         
6330         // show a panel if it's registered and related..
6331         
6332         if (!this.navId || !this.tabId || !state || is_was_active) {
6333             return;
6334         }
6335         
6336         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6337         if (!tg) {
6338             return;
6339         }
6340         var pan = tg.getPanelByName(this.tabId);
6341         if (!pan) {
6342             return;
6343         }
6344         // if we can not flip to new panel - go back to old nav highlight..
6345         if (false == tg.showPanel(pan)) {
6346             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6347             if (nv) {
6348                 var onav = nv.getWasActive();
6349                 if (onav) {
6350                     onav.setActive(true, false, true);
6351                 }
6352             }
6353             
6354         }
6355         
6356         
6357         
6358     },
6359      // this should not be here...
6360     setDisabled : function(state)
6361     {
6362         this.disabled = state;
6363         if (!state ) {
6364             this.el.removeClass('disabled');
6365         } else if (!this.el.hasClass('disabled')) {
6366             this.el.addClass('disabled');
6367         }
6368         
6369     },
6370     
6371     /**
6372      * Fetch the element to display the tooltip on.
6373      * @return {Roo.Element} defaults to this.el
6374      */
6375     tooltipEl : function()
6376     {
6377         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6378     },
6379     
6380     scrollToElement : function(e)
6381     {
6382         var c = document.body;
6383         
6384         /*
6385          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6386          */
6387         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6388             c = document.documentElement;
6389         }
6390         
6391         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6392         
6393         if(!target){
6394             return;
6395         }
6396
6397         var o = target.calcOffsetsTo(c);
6398         
6399         var options = {
6400             target : target,
6401             value : o[1]
6402         };
6403         
6404         this.fireEvent('scrollto', this, options, e);
6405         
6406         Roo.get(c).scrollTo('top', options.value, true);
6407         
6408         return;
6409     }
6410 });
6411  
6412
6413  /*
6414  * - LGPL
6415  *
6416  * sidebar item
6417  *
6418  *  li
6419  *    <span> icon </span>
6420  *    <span> text </span>
6421  *    <span>badge </span>
6422  */
6423
6424 /**
6425  * @class Roo.bootstrap.NavSidebarItem
6426  * @extends Roo.bootstrap.NavItem
6427  * Bootstrap Navbar.NavSidebarItem class
6428  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6429  * {Boolean} open is the menu open
6430  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6431  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6432  * {String} buttonSize (sm|md|lg)the extra classes for the button
6433  * {Boolean} showArrow show arrow next to the text (default true)
6434  * @constructor
6435  * Create a new Navbar Button
6436  * @param {Object} config The config object
6437  */
6438 Roo.bootstrap.NavSidebarItem = function(config){
6439     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6440     this.addEvents({
6441         // raw events
6442         /**
6443          * @event click
6444          * The raw click event for the entire grid.
6445          * @param {Roo.EventObject} e
6446          */
6447         "click" : true,
6448          /**
6449             * @event changed
6450             * Fires when the active item active state changes
6451             * @param {Roo.bootstrap.NavSidebarItem} this
6452             * @param {boolean} state the new state
6453              
6454          */
6455         'changed': true
6456     });
6457    
6458 };
6459
6460 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6461     
6462     badgeWeight : 'default',
6463     
6464     open: false,
6465     
6466     buttonView : false,
6467     
6468     buttonWeight : 'default',
6469     
6470     buttonSize : 'md',
6471     
6472     showArrow : true,
6473     
6474     getAutoCreate : function(){
6475         
6476         
6477         var a = {
6478                 tag: 'a',
6479                 href : this.href || '#',
6480                 cls: '',
6481                 html : '',
6482                 cn : []
6483         };
6484         
6485         if(this.buttonView){
6486             a = {
6487                 tag: 'button',
6488                 href : this.href || '#',
6489                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6490                 html : this.html,
6491                 cn : []
6492             };
6493         }
6494         
6495         var cfg = {
6496             tag: 'li',
6497             cls: '',
6498             cn: [ a ]
6499         };
6500         
6501         if (this.active) {
6502             cfg.cls += ' active';
6503         }
6504         
6505         if (this.disabled) {
6506             cfg.cls += ' disabled';
6507         }
6508         if (this.open) {
6509             cfg.cls += ' open x-open';
6510         }
6511         // left icon..
6512         if (this.glyphicon || this.icon) {
6513             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6514             a.cn.push({ tag : 'i', cls : c }) ;
6515         }
6516         
6517         if(!this.buttonView){
6518             var span = {
6519                 tag: 'span',
6520                 html : this.html || ''
6521             };
6522
6523             a.cn.push(span);
6524             
6525         }
6526         
6527         if (this.badge !== '') {
6528             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6529         }
6530         
6531         if (this.menu) {
6532             
6533             if(this.showArrow){
6534                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6535             }
6536             
6537             a.cls += ' dropdown-toggle treeview' ;
6538         }
6539         
6540         return cfg;
6541     },
6542     
6543     initEvents : function()
6544     { 
6545         if (typeof (this.menu) != 'undefined') {
6546             this.menu.parentType = this.xtype;
6547             this.menu.triggerEl = this.el;
6548             this.menu = this.addxtype(Roo.apply({}, this.menu));
6549         }
6550         
6551         this.el.on('click', this.onClick, this);
6552         
6553         if(this.badge !== ''){
6554             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6555         }
6556         
6557     },
6558     
6559     onClick : function(e)
6560     {
6561         if(this.disabled){
6562             e.preventDefault();
6563             return;
6564         }
6565         
6566         if(this.preventDefault){
6567             e.preventDefault();
6568         }
6569         
6570         this.fireEvent('click', this, e);
6571     },
6572     
6573     disable : function()
6574     {
6575         this.setDisabled(true);
6576     },
6577     
6578     enable : function()
6579     {
6580         this.setDisabled(false);
6581     },
6582     
6583     setDisabled : function(state)
6584     {
6585         if(this.disabled == state){
6586             return;
6587         }
6588         
6589         this.disabled = state;
6590         
6591         if (state) {
6592             this.el.addClass('disabled');
6593             return;
6594         }
6595         
6596         this.el.removeClass('disabled');
6597         
6598         return;
6599     },
6600     
6601     setActive : function(state)
6602     {
6603         if(this.active == state){
6604             return;
6605         }
6606         
6607         this.active = state;
6608         
6609         if (state) {
6610             this.el.addClass('active');
6611             return;
6612         }
6613         
6614         this.el.removeClass('active');
6615         
6616         return;
6617     },
6618     
6619     isActive: function () 
6620     {
6621         return this.active;
6622     },
6623     
6624     setBadge : function(str)
6625     {
6626         if(!this.badgeEl){
6627             return;
6628         }
6629         
6630         this.badgeEl.dom.innerHTML = str;
6631     }
6632     
6633    
6634      
6635  
6636 });
6637  
6638
6639  /*
6640  * - LGPL
6641  *
6642  *  Breadcrumb Nav
6643  * 
6644  */
6645 Roo.namespace('Roo.bootstrap.breadcrumb');
6646
6647
6648 /**
6649  * @class Roo.bootstrap.breadcrumb.Nav
6650  * @extends Roo.bootstrap.Component
6651  * Bootstrap Breadcrumb Nav Class
6652  *  
6653  * @children Roo.bootstrap.breadcrumb.Item
6654  * 
6655  * @constructor
6656  * Create a new breadcrumb.Nav
6657  * @param {Object} config The config object
6658  */
6659
6660
6661 Roo.bootstrap.breadcrumb.Nav = function(config){
6662     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6663     
6664     
6665 };
6666
6667 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6668     
6669     getAutoCreate : function()
6670     {
6671
6672         var cfg = {
6673             tag: 'nav',
6674             cn : [
6675                 {
6676                     tag : 'ol',
6677                     cls : 'breadcrumb'
6678                 }
6679             ]
6680             
6681         };
6682           
6683         return cfg;
6684     },
6685     
6686     initEvents: function()
6687     {
6688         this.olEl = this.el.select('ol',true).first();    
6689     },
6690     getChildContainer : function()
6691     {
6692         return this.olEl;  
6693     }
6694     
6695 });
6696
6697  /*
6698  * - LGPL
6699  *
6700  *  Breadcrumb Item
6701  * 
6702  */
6703
6704
6705 /**
6706  * @class Roo.bootstrap.breadcrumb.Nav
6707  * @extends Roo.bootstrap.Component
6708  * Bootstrap Breadcrumb Nav Class
6709  *  
6710  * @children Roo.bootstrap.breadcrumb.Component
6711  * @cfg {String} html the content of the link.
6712  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6713  * @cfg {Boolean} active is it active
6714
6715  * 
6716  * @constructor
6717  * Create a new breadcrumb.Nav
6718  * @param {Object} config The config object
6719  */
6720
6721 Roo.bootstrap.breadcrumb.Item = function(config){
6722     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6723     this.addEvents({
6724         // img events
6725         /**
6726          * @event click
6727          * The img click event for the img.
6728          * @param {Roo.EventObject} e
6729          */
6730         "click" : true
6731     });
6732     
6733 };
6734
6735 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6736     
6737     href: false,
6738     html : '',
6739     
6740     getAutoCreate : function()
6741     {
6742
6743         var cfg = {
6744             tag: 'li',
6745             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6746         };
6747         if (this.href !== false) {
6748             cfg.cn = [{
6749                 tag : 'a',
6750                 href : this.href,
6751                 html : this.html
6752             }];
6753         } else {
6754             cfg.html = this.html;
6755         }
6756         
6757         return cfg;
6758     },
6759     
6760     initEvents: function()
6761     {
6762         if (this.href) {
6763             this.el.select('a', true).first().on('click',this.onClick, this)
6764         }
6765         
6766     },
6767     onClick : function(e)
6768     {
6769         e.preventDefault();
6770         this.fireEvent('click',this,  e);
6771     }
6772     
6773 });
6774
6775  /*
6776  * - LGPL
6777  *
6778  * row
6779  * 
6780  */
6781
6782 /**
6783  * @class Roo.bootstrap.Row
6784  * @extends Roo.bootstrap.Component
6785  * Bootstrap Row class (contains columns...)
6786  * 
6787  * @constructor
6788  * Create a new Row
6789  * @param {Object} config The config object
6790  */
6791
6792 Roo.bootstrap.Row = function(config){
6793     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6794 };
6795
6796 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6797     
6798     getAutoCreate : function(){
6799        return {
6800             cls: 'row clearfix'
6801        };
6802     }
6803     
6804     
6805 });
6806
6807  
6808
6809  /*
6810  * - LGPL
6811  *
6812  * pagination
6813  * 
6814  */
6815
6816 /**
6817  * @class Roo.bootstrap.Pagination
6818  * @extends Roo.bootstrap.Component
6819  * Bootstrap Pagination class
6820  * @cfg {String} size xs | sm | md | lg
6821  * @cfg {Boolean} inverse false | true
6822  * 
6823  * @constructor
6824  * Create a new Pagination
6825  * @param {Object} config The config object
6826  */
6827
6828 Roo.bootstrap.Pagination = function(config){
6829     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6830 };
6831
6832 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6833     
6834     cls: false,
6835     size: false,
6836     inverse: false,
6837     
6838     getAutoCreate : function(){
6839         var cfg = {
6840             tag: 'ul',
6841                 cls: 'pagination'
6842         };
6843         if (this.inverse) {
6844             cfg.cls += ' inverse';
6845         }
6846         if (this.html) {
6847             cfg.html=this.html;
6848         }
6849         if (this.cls) {
6850             cfg.cls += " " + this.cls;
6851         }
6852         return cfg;
6853     }
6854    
6855 });
6856
6857  
6858
6859  /*
6860  * - LGPL
6861  *
6862  * Pagination item
6863  * 
6864  */
6865
6866
6867 /**
6868  * @class Roo.bootstrap.PaginationItem
6869  * @extends Roo.bootstrap.Component
6870  * Bootstrap PaginationItem class
6871  * @cfg {String} html text
6872  * @cfg {String} href the link
6873  * @cfg {Boolean} preventDefault (true | false) default true
6874  * @cfg {Boolean} active (true | false) default false
6875  * @cfg {Boolean} disabled default false
6876  * 
6877  * 
6878  * @constructor
6879  * Create a new PaginationItem
6880  * @param {Object} config The config object
6881  */
6882
6883
6884 Roo.bootstrap.PaginationItem = function(config){
6885     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6886     this.addEvents({
6887         // raw events
6888         /**
6889          * @event click
6890          * The raw click event for the entire grid.
6891          * @param {Roo.EventObject} e
6892          */
6893         "click" : true
6894     });
6895 };
6896
6897 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6898     
6899     href : false,
6900     html : false,
6901     preventDefault: true,
6902     active : false,
6903     cls : false,
6904     disabled: false,
6905     
6906     getAutoCreate : function(){
6907         var cfg= {
6908             tag: 'li',
6909             cn: [
6910                 {
6911                     tag : 'a',
6912                     href : this.href ? this.href : '#',
6913                     html : this.html ? this.html : ''
6914                 }
6915             ]
6916         };
6917         
6918         if(this.cls){
6919             cfg.cls = this.cls;
6920         }
6921         
6922         if(this.disabled){
6923             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6924         }
6925         
6926         if(this.active){
6927             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6928         }
6929         
6930         return cfg;
6931     },
6932     
6933     initEvents: function() {
6934         
6935         this.el.on('click', this.onClick, this);
6936         
6937     },
6938     onClick : function(e)
6939     {
6940         Roo.log('PaginationItem on click ');
6941         if(this.preventDefault){
6942             e.preventDefault();
6943         }
6944         
6945         if(this.disabled){
6946             return;
6947         }
6948         
6949         this.fireEvent('click', this, e);
6950     }
6951    
6952 });
6953
6954  
6955
6956  /*
6957  * - LGPL
6958  *
6959  * slider
6960  * 
6961  */
6962
6963
6964 /**
6965  * @class Roo.bootstrap.Slider
6966  * @extends Roo.bootstrap.Component
6967  * Bootstrap Slider class
6968  *    
6969  * @constructor
6970  * Create a new Slider
6971  * @param {Object} config The config object
6972  */
6973
6974 Roo.bootstrap.Slider = function(config){
6975     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6976 };
6977
6978 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6979     
6980     getAutoCreate : function(){
6981         
6982         var cfg = {
6983             tag: 'div',
6984             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6985             cn: [
6986                 {
6987                     tag: 'a',
6988                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6989                 }
6990             ]
6991         };
6992         
6993         return cfg;
6994     }
6995    
6996 });
6997
6998  /*
6999  * Based on:
7000  * Ext JS Library 1.1.1
7001  * Copyright(c) 2006-2007, Ext JS, LLC.
7002  *
7003  * Originally Released Under LGPL - original licence link has changed is not relivant.
7004  *
7005  * Fork - LGPL
7006  * <script type="text/javascript">
7007  */
7008  
7009
7010 /**
7011  * @class Roo.grid.ColumnModel
7012  * @extends Roo.util.Observable
7013  * This is the default implementation of a ColumnModel used by the Grid. It defines
7014  * the columns in the grid.
7015  * <br>Usage:<br>
7016  <pre><code>
7017  var colModel = new Roo.grid.ColumnModel([
7018         {header: "Ticker", width: 60, sortable: true, locked: true},
7019         {header: "Company Name", width: 150, sortable: true},
7020         {header: "Market Cap.", width: 100, sortable: true},
7021         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7022         {header: "Employees", width: 100, sortable: true, resizable: false}
7023  ]);
7024  </code></pre>
7025  * <p>
7026  
7027  * The config options listed for this class are options which may appear in each
7028  * individual column definition.
7029  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7030  * @constructor
7031  * @param {Object} config An Array of column config objects. See this class's
7032  * config objects for details.
7033 */
7034 Roo.grid.ColumnModel = function(config){
7035         /**
7036      * The config passed into the constructor
7037      */
7038     this.config = config;
7039     this.lookup = {};
7040
7041     // if no id, create one
7042     // if the column does not have a dataIndex mapping,
7043     // map it to the order it is in the config
7044     for(var i = 0, len = config.length; i < len; i++){
7045         var c = config[i];
7046         if(typeof c.dataIndex == "undefined"){
7047             c.dataIndex = i;
7048         }
7049         if(typeof c.renderer == "string"){
7050             c.renderer = Roo.util.Format[c.renderer];
7051         }
7052         if(typeof c.id == "undefined"){
7053             c.id = Roo.id();
7054         }
7055         if(c.editor && c.editor.xtype){
7056             c.editor  = Roo.factory(c.editor, Roo.grid);
7057         }
7058         if(c.editor && c.editor.isFormField){
7059             c.editor = new Roo.grid.GridEditor(c.editor);
7060         }
7061         this.lookup[c.id] = c;
7062     }
7063
7064     /**
7065      * The width of columns which have no width specified (defaults to 100)
7066      * @type Number
7067      */
7068     this.defaultWidth = 100;
7069
7070     /**
7071      * Default sortable of columns which have no sortable specified (defaults to false)
7072      * @type Boolean
7073      */
7074     this.defaultSortable = false;
7075
7076     this.addEvents({
7077         /**
7078              * @event widthchange
7079              * Fires when the width of a column changes.
7080              * @param {ColumnModel} this
7081              * @param {Number} columnIndex The column index
7082              * @param {Number} newWidth The new width
7083              */
7084             "widthchange": true,
7085         /**
7086              * @event headerchange
7087              * Fires when the text of a header changes.
7088              * @param {ColumnModel} this
7089              * @param {Number} columnIndex The column index
7090              * @param {Number} newText The new header text
7091              */
7092             "headerchange": true,
7093         /**
7094              * @event hiddenchange
7095              * Fires when a column is hidden or "unhidden".
7096              * @param {ColumnModel} this
7097              * @param {Number} columnIndex The column index
7098              * @param {Boolean} hidden true if hidden, false otherwise
7099              */
7100             "hiddenchange": true,
7101             /**
7102          * @event columnmoved
7103          * Fires when a column is moved.
7104          * @param {ColumnModel} this
7105          * @param {Number} oldIndex
7106          * @param {Number} newIndex
7107          */
7108         "columnmoved" : true,
7109         /**
7110          * @event columlockchange
7111          * Fires when a column's locked state is changed
7112          * @param {ColumnModel} this
7113          * @param {Number} colIndex
7114          * @param {Boolean} locked true if locked
7115          */
7116         "columnlockchange" : true
7117     });
7118     Roo.grid.ColumnModel.superclass.constructor.call(this);
7119 };
7120 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7121     /**
7122      * @cfg {String} header The header text to display in the Grid view.
7123      */
7124     /**
7125      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7126      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7127      * specified, the column's index is used as an index into the Record's data Array.
7128      */
7129     /**
7130      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7131      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7132      */
7133     /**
7134      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7135      * Defaults to the value of the {@link #defaultSortable} property.
7136      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7137      */
7138     /**
7139      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7140      */
7141     /**
7142      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7143      */
7144     /**
7145      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7146      */
7147     /**
7148      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7149      */
7150     /**
7151      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7152      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7153      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7154      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7155      */
7156        /**
7157      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7158      */
7159     /**
7160      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7161      */
7162     /**
7163      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7164      */
7165     /**
7166      * @cfg {String} cursor (Optional)
7167      */
7168     /**
7169      * @cfg {String} tooltip (Optional)
7170      */
7171     /**
7172      * @cfg {Number} xs (Optional)
7173      */
7174     /**
7175      * @cfg {Number} sm (Optional)
7176      */
7177     /**
7178      * @cfg {Number} md (Optional)
7179      */
7180     /**
7181      * @cfg {Number} lg (Optional)
7182      */
7183     /**
7184      * Returns the id of the column at the specified index.
7185      * @param {Number} index The column index
7186      * @return {String} the id
7187      */
7188     getColumnId : function(index){
7189         return this.config[index].id;
7190     },
7191
7192     /**
7193      * Returns the column for a specified id.
7194      * @param {String} id The column id
7195      * @return {Object} the column
7196      */
7197     getColumnById : function(id){
7198         return this.lookup[id];
7199     },
7200
7201     
7202     /**
7203      * Returns the column for a specified dataIndex.
7204      * @param {String} dataIndex The column dataIndex
7205      * @return {Object|Boolean} the column or false if not found
7206      */
7207     getColumnByDataIndex: function(dataIndex){
7208         var index = this.findColumnIndex(dataIndex);
7209         return index > -1 ? this.config[index] : false;
7210     },
7211     
7212     /**
7213      * Returns the index for a specified column id.
7214      * @param {String} id The column id
7215      * @return {Number} the index, or -1 if not found
7216      */
7217     getIndexById : function(id){
7218         for(var i = 0, len = this.config.length; i < len; i++){
7219             if(this.config[i].id == id){
7220                 return i;
7221             }
7222         }
7223         return -1;
7224     },
7225     
7226     /**
7227      * Returns the index for a specified column dataIndex.
7228      * @param {String} dataIndex The column dataIndex
7229      * @return {Number} the index, or -1 if not found
7230      */
7231     
7232     findColumnIndex : function(dataIndex){
7233         for(var i = 0, len = this.config.length; i < len; i++){
7234             if(this.config[i].dataIndex == dataIndex){
7235                 return i;
7236             }
7237         }
7238         return -1;
7239     },
7240     
7241     
7242     moveColumn : function(oldIndex, newIndex){
7243         var c = this.config[oldIndex];
7244         this.config.splice(oldIndex, 1);
7245         this.config.splice(newIndex, 0, c);
7246         this.dataMap = null;
7247         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7248     },
7249
7250     isLocked : function(colIndex){
7251         return this.config[colIndex].locked === true;
7252     },
7253
7254     setLocked : function(colIndex, value, suppressEvent){
7255         if(this.isLocked(colIndex) == value){
7256             return;
7257         }
7258         this.config[colIndex].locked = value;
7259         if(!suppressEvent){
7260             this.fireEvent("columnlockchange", this, colIndex, value);
7261         }
7262     },
7263
7264     getTotalLockedWidth : function(){
7265         var totalWidth = 0;
7266         for(var i = 0; i < this.config.length; i++){
7267             if(this.isLocked(i) && !this.isHidden(i)){
7268                 this.totalWidth += this.getColumnWidth(i);
7269             }
7270         }
7271         return totalWidth;
7272     },
7273
7274     getLockedCount : function(){
7275         for(var i = 0, len = this.config.length; i < len; i++){
7276             if(!this.isLocked(i)){
7277                 return i;
7278             }
7279         }
7280         
7281         return this.config.length;
7282     },
7283
7284     /**
7285      * Returns the number of columns.
7286      * @return {Number}
7287      */
7288     getColumnCount : function(visibleOnly){
7289         if(visibleOnly === true){
7290             var c = 0;
7291             for(var i = 0, len = this.config.length; i < len; i++){
7292                 if(!this.isHidden(i)){
7293                     c++;
7294                 }
7295             }
7296             return c;
7297         }
7298         return this.config.length;
7299     },
7300
7301     /**
7302      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7303      * @param {Function} fn
7304      * @param {Object} scope (optional)
7305      * @return {Array} result
7306      */
7307     getColumnsBy : function(fn, scope){
7308         var r = [];
7309         for(var i = 0, len = this.config.length; i < len; i++){
7310             var c = this.config[i];
7311             if(fn.call(scope||this, c, i) === true){
7312                 r[r.length] = c;
7313             }
7314         }
7315         return r;
7316     },
7317
7318     /**
7319      * Returns true if the specified column is sortable.
7320      * @param {Number} col The column index
7321      * @return {Boolean}
7322      */
7323     isSortable : function(col){
7324         if(typeof this.config[col].sortable == "undefined"){
7325             return this.defaultSortable;
7326         }
7327         return this.config[col].sortable;
7328     },
7329
7330     /**
7331      * Returns the rendering (formatting) function defined for the column.
7332      * @param {Number} col The column index.
7333      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7334      */
7335     getRenderer : function(col){
7336         if(!this.config[col].renderer){
7337             return Roo.grid.ColumnModel.defaultRenderer;
7338         }
7339         return this.config[col].renderer;
7340     },
7341
7342     /**
7343      * Sets the rendering (formatting) function for a column.
7344      * @param {Number} col The column index
7345      * @param {Function} fn The function to use to process the cell's raw data
7346      * to return HTML markup for the grid view. The render function is called with
7347      * the following parameters:<ul>
7348      * <li>Data value.</li>
7349      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7350      * <li>css A CSS style string to apply to the table cell.</li>
7351      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7352      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7353      * <li>Row index</li>
7354      * <li>Column index</li>
7355      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7356      */
7357     setRenderer : function(col, fn){
7358         this.config[col].renderer = fn;
7359     },
7360
7361     /**
7362      * Returns the width for the specified column.
7363      * @param {Number} col The column index
7364      * @return {Number}
7365      */
7366     getColumnWidth : function(col){
7367         return this.config[col].width * 1 || this.defaultWidth;
7368     },
7369
7370     /**
7371      * Sets the width for a column.
7372      * @param {Number} col The column index
7373      * @param {Number} width The new width
7374      */
7375     setColumnWidth : function(col, width, suppressEvent){
7376         this.config[col].width = width;
7377         this.totalWidth = null;
7378         if(!suppressEvent){
7379              this.fireEvent("widthchange", this, col, width);
7380         }
7381     },
7382
7383     /**
7384      * Returns the total width of all columns.
7385      * @param {Boolean} includeHidden True to include hidden column widths
7386      * @return {Number}
7387      */
7388     getTotalWidth : function(includeHidden){
7389         if(!this.totalWidth){
7390             this.totalWidth = 0;
7391             for(var i = 0, len = this.config.length; i < len; i++){
7392                 if(includeHidden || !this.isHidden(i)){
7393                     this.totalWidth += this.getColumnWidth(i);
7394                 }
7395             }
7396         }
7397         return this.totalWidth;
7398     },
7399
7400     /**
7401      * Returns the header for the specified column.
7402      * @param {Number} col The column index
7403      * @return {String}
7404      */
7405     getColumnHeader : function(col){
7406         return this.config[col].header;
7407     },
7408
7409     /**
7410      * Sets the header for a column.
7411      * @param {Number} col The column index
7412      * @param {String} header The new header
7413      */
7414     setColumnHeader : function(col, header){
7415         this.config[col].header = header;
7416         this.fireEvent("headerchange", this, col, header);
7417     },
7418
7419     /**
7420      * Returns the tooltip for the specified column.
7421      * @param {Number} col The column index
7422      * @return {String}
7423      */
7424     getColumnTooltip : function(col){
7425             return this.config[col].tooltip;
7426     },
7427     /**
7428      * Sets the tooltip for a column.
7429      * @param {Number} col The column index
7430      * @param {String} tooltip The new tooltip
7431      */
7432     setColumnTooltip : function(col, tooltip){
7433             this.config[col].tooltip = tooltip;
7434     },
7435
7436     /**
7437      * Returns the dataIndex for the specified column.
7438      * @param {Number} col The column index
7439      * @return {Number}
7440      */
7441     getDataIndex : function(col){
7442         return this.config[col].dataIndex;
7443     },
7444
7445     /**
7446      * Sets the dataIndex for a column.
7447      * @param {Number} col The column index
7448      * @param {Number} dataIndex The new dataIndex
7449      */
7450     setDataIndex : function(col, dataIndex){
7451         this.config[col].dataIndex = dataIndex;
7452     },
7453
7454     
7455     
7456     /**
7457      * Returns true if the cell is editable.
7458      * @param {Number} colIndex The column index
7459      * @param {Number} rowIndex The row index - this is nto actually used..?
7460      * @return {Boolean}
7461      */
7462     isCellEditable : function(colIndex, rowIndex){
7463         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7464     },
7465
7466     /**
7467      * Returns the editor defined for the cell/column.
7468      * return false or null to disable editing.
7469      * @param {Number} colIndex The column index
7470      * @param {Number} rowIndex The row index
7471      * @return {Object}
7472      */
7473     getCellEditor : function(colIndex, rowIndex){
7474         return this.config[colIndex].editor;
7475     },
7476
7477     /**
7478      * Sets if a column is editable.
7479      * @param {Number} col The column index
7480      * @param {Boolean} editable True if the column is editable
7481      */
7482     setEditable : function(col, editable){
7483         this.config[col].editable = editable;
7484     },
7485
7486
7487     /**
7488      * Returns true if the column is hidden.
7489      * @param {Number} colIndex The column index
7490      * @return {Boolean}
7491      */
7492     isHidden : function(colIndex){
7493         return this.config[colIndex].hidden;
7494     },
7495
7496
7497     /**
7498      * Returns true if the column width cannot be changed
7499      */
7500     isFixed : function(colIndex){
7501         return this.config[colIndex].fixed;
7502     },
7503
7504     /**
7505      * Returns true if the column can be resized
7506      * @return {Boolean}
7507      */
7508     isResizable : function(colIndex){
7509         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7510     },
7511     /**
7512      * Sets if a column is hidden.
7513      * @param {Number} colIndex The column index
7514      * @param {Boolean} hidden True if the column is hidden
7515      */
7516     setHidden : function(colIndex, hidden){
7517         this.config[colIndex].hidden = hidden;
7518         this.totalWidth = null;
7519         this.fireEvent("hiddenchange", this, colIndex, hidden);
7520     },
7521
7522     /**
7523      * Sets the editor for a column.
7524      * @param {Number} col The column index
7525      * @param {Object} editor The editor object
7526      */
7527     setEditor : function(col, editor){
7528         this.config[col].editor = editor;
7529     }
7530 });
7531
7532 Roo.grid.ColumnModel.defaultRenderer = function(value)
7533 {
7534     if(typeof value == "object") {
7535         return value;
7536     }
7537         if(typeof value == "string" && value.length < 1){
7538             return "&#160;";
7539         }
7540     
7541         return String.format("{0}", value);
7542 };
7543
7544 // Alias for backwards compatibility
7545 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7546 /*
7547  * Based on:
7548  * Ext JS Library 1.1.1
7549  * Copyright(c) 2006-2007, Ext JS, LLC.
7550  *
7551  * Originally Released Under LGPL - original licence link has changed is not relivant.
7552  *
7553  * Fork - LGPL
7554  * <script type="text/javascript">
7555  */
7556  
7557 /**
7558  * @class Roo.LoadMask
7559  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7560  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7561  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7562  * element's UpdateManager load indicator and will be destroyed after the initial load.
7563  * @constructor
7564  * Create a new LoadMask
7565  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7566  * @param {Object} config The config object
7567  */
7568 Roo.LoadMask = function(el, config){
7569     this.el = Roo.get(el);
7570     Roo.apply(this, config);
7571     if(this.store){
7572         this.store.on('beforeload', this.onBeforeLoad, this);
7573         this.store.on('load', this.onLoad, this);
7574         this.store.on('loadexception', this.onLoadException, this);
7575         this.removeMask = false;
7576     }else{
7577         var um = this.el.getUpdateManager();
7578         um.showLoadIndicator = false; // disable the default indicator
7579         um.on('beforeupdate', this.onBeforeLoad, this);
7580         um.on('update', this.onLoad, this);
7581         um.on('failure', this.onLoad, this);
7582         this.removeMask = true;
7583     }
7584 };
7585
7586 Roo.LoadMask.prototype = {
7587     /**
7588      * @cfg {Boolean} removeMask
7589      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7590      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7591      */
7592     /**
7593      * @cfg {String} msg
7594      * The text to display in a centered loading message box (defaults to 'Loading...')
7595      */
7596     msg : 'Loading...',
7597     /**
7598      * @cfg {String} msgCls
7599      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7600      */
7601     msgCls : 'x-mask-loading',
7602
7603     /**
7604      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7605      * @type Boolean
7606      */
7607     disabled: false,
7608
7609     /**
7610      * Disables the mask to prevent it from being displayed
7611      */
7612     disable : function(){
7613        this.disabled = true;
7614     },
7615
7616     /**
7617      * Enables the mask so that it can be displayed
7618      */
7619     enable : function(){
7620         this.disabled = false;
7621     },
7622     
7623     onLoadException : function()
7624     {
7625         Roo.log(arguments);
7626         
7627         if (typeof(arguments[3]) != 'undefined') {
7628             Roo.MessageBox.alert("Error loading",arguments[3]);
7629         } 
7630         /*
7631         try {
7632             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7633                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7634             }   
7635         } catch(e) {
7636             
7637         }
7638         */
7639     
7640         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7641     },
7642     // private
7643     onLoad : function()
7644     {
7645         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7646     },
7647
7648     // private
7649     onBeforeLoad : function(){
7650         if(!this.disabled){
7651             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7652         }
7653     },
7654
7655     // private
7656     destroy : function(){
7657         if(this.store){
7658             this.store.un('beforeload', this.onBeforeLoad, this);
7659             this.store.un('load', this.onLoad, this);
7660             this.store.un('loadexception', this.onLoadException, this);
7661         }else{
7662             var um = this.el.getUpdateManager();
7663             um.un('beforeupdate', this.onBeforeLoad, this);
7664             um.un('update', this.onLoad, this);
7665             um.un('failure', this.onLoad, this);
7666         }
7667     }
7668 };/*
7669  * - LGPL
7670  *
7671  * table
7672  * 
7673  */
7674
7675 /**
7676  * @class Roo.bootstrap.Table
7677  * @extends Roo.bootstrap.Component
7678  * Bootstrap Table class
7679  * @cfg {String} cls table class
7680  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7681  * @cfg {String} bgcolor Specifies the background color for a table
7682  * @cfg {Number} border Specifies whether the table cells should have borders or not
7683  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7684  * @cfg {Number} cellspacing Specifies the space between cells
7685  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7686  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7687  * @cfg {String} sortable Specifies that the table should be sortable
7688  * @cfg {String} summary Specifies a summary of the content of a table
7689  * @cfg {Number} width Specifies the width of a table
7690  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7691  * 
7692  * @cfg {boolean} striped Should the rows be alternative striped
7693  * @cfg {boolean} bordered Add borders to the table
7694  * @cfg {boolean} hover Add hover highlighting
7695  * @cfg {boolean} condensed Format condensed
7696  * @cfg {boolean} responsive Format condensed
7697  * @cfg {Boolean} loadMask (true|false) default false
7698  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7699  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7700  * @cfg {Boolean} rowSelection (true|false) default false
7701  * @cfg {Boolean} cellSelection (true|false) default false
7702  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7703  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7704  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7705  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7706  
7707  * 
7708  * @constructor
7709  * Create a new Table
7710  * @param {Object} config The config object
7711  */
7712
7713 Roo.bootstrap.Table = function(config){
7714     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7715     
7716   
7717     
7718     // BC...
7719     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7720     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7721     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7722     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7723     
7724     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7725     if (this.sm) {
7726         this.sm.grid = this;
7727         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7728         this.sm = this.selModel;
7729         this.sm.xmodule = this.xmodule || false;
7730     }
7731     
7732     if (this.cm && typeof(this.cm.config) == 'undefined') {
7733         this.colModel = new Roo.grid.ColumnModel(this.cm);
7734         this.cm = this.colModel;
7735         this.cm.xmodule = this.xmodule || false;
7736     }
7737     if (this.store) {
7738         this.store= Roo.factory(this.store, Roo.data);
7739         this.ds = this.store;
7740         this.ds.xmodule = this.xmodule || false;
7741          
7742     }
7743     if (this.footer && this.store) {
7744         this.footer.dataSource = this.ds;
7745         this.footer = Roo.factory(this.footer);
7746     }
7747     
7748     /** @private */
7749     this.addEvents({
7750         /**
7751          * @event cellclick
7752          * Fires when a cell is clicked
7753          * @param {Roo.bootstrap.Table} this
7754          * @param {Roo.Element} el
7755          * @param {Number} rowIndex
7756          * @param {Number} columnIndex
7757          * @param {Roo.EventObject} e
7758          */
7759         "cellclick" : true,
7760         /**
7761          * @event celldblclick
7762          * Fires when a cell is double clicked
7763          * @param {Roo.bootstrap.Table} this
7764          * @param {Roo.Element} el
7765          * @param {Number} rowIndex
7766          * @param {Number} columnIndex
7767          * @param {Roo.EventObject} e
7768          */
7769         "celldblclick" : true,
7770         /**
7771          * @event rowclick
7772          * Fires when a row is clicked
7773          * @param {Roo.bootstrap.Table} this
7774          * @param {Roo.Element} el
7775          * @param {Number} rowIndex
7776          * @param {Roo.EventObject} e
7777          */
7778         "rowclick" : true,
7779         /**
7780          * @event rowdblclick
7781          * Fires when a row is double clicked
7782          * @param {Roo.bootstrap.Table} this
7783          * @param {Roo.Element} el
7784          * @param {Number} rowIndex
7785          * @param {Roo.EventObject} e
7786          */
7787         "rowdblclick" : true,
7788         /**
7789          * @event mouseover
7790          * Fires when a mouseover occur
7791          * @param {Roo.bootstrap.Table} this
7792          * @param {Roo.Element} el
7793          * @param {Number} rowIndex
7794          * @param {Number} columnIndex
7795          * @param {Roo.EventObject} e
7796          */
7797         "mouseover" : true,
7798         /**
7799          * @event mouseout
7800          * Fires when a mouseout occur
7801          * @param {Roo.bootstrap.Table} this
7802          * @param {Roo.Element} el
7803          * @param {Number} rowIndex
7804          * @param {Number} columnIndex
7805          * @param {Roo.EventObject} e
7806          */
7807         "mouseout" : true,
7808         /**
7809          * @event rowclass
7810          * Fires when a row is rendered, so you can change add a style to it.
7811          * @param {Roo.bootstrap.Table} this
7812          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7813          */
7814         'rowclass' : true,
7815           /**
7816          * @event rowsrendered
7817          * Fires when all the  rows have been rendered
7818          * @param {Roo.bootstrap.Table} this
7819          */
7820         'rowsrendered' : true,
7821         /**
7822          * @event contextmenu
7823          * The raw contextmenu event for the entire grid.
7824          * @param {Roo.EventObject} e
7825          */
7826         "contextmenu" : true,
7827         /**
7828          * @event rowcontextmenu
7829          * Fires when a row is right clicked
7830          * @param {Roo.bootstrap.Table} this
7831          * @param {Number} rowIndex
7832          * @param {Roo.EventObject} e
7833          */
7834         "rowcontextmenu" : true,
7835         /**
7836          * @event cellcontextmenu
7837          * Fires when a cell is right clicked
7838          * @param {Roo.bootstrap.Table} this
7839          * @param {Number} rowIndex
7840          * @param {Number} cellIndex
7841          * @param {Roo.EventObject} e
7842          */
7843          "cellcontextmenu" : true,
7844          /**
7845          * @event headercontextmenu
7846          * Fires when a header is right clicked
7847          * @param {Roo.bootstrap.Table} this
7848          * @param {Number} columnIndex
7849          * @param {Roo.EventObject} e
7850          */
7851         "headercontextmenu" : true
7852     });
7853 };
7854
7855 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7856     
7857     cls: false,
7858     align: false,
7859     bgcolor: false,
7860     border: false,
7861     cellpadding: false,
7862     cellspacing: false,
7863     frame: false,
7864     rules: false,
7865     sortable: false,
7866     summary: false,
7867     width: false,
7868     striped : false,
7869     scrollBody : false,
7870     bordered: false,
7871     hover:  false,
7872     condensed : false,
7873     responsive : false,
7874     sm : false,
7875     cm : false,
7876     store : false,
7877     loadMask : false,
7878     footerShow : true,
7879     headerShow : true,
7880   
7881     rowSelection : false,
7882     cellSelection : false,
7883     layout : false,
7884     
7885     // Roo.Element - the tbody
7886     mainBody: false,
7887     // Roo.Element - thead element
7888     mainHead: false,
7889     
7890     container: false, // used by gridpanel...
7891     
7892     lazyLoad : false,
7893     
7894     CSS : Roo.util.CSS,
7895     
7896     auto_hide_footer : false,
7897     
7898     getAutoCreate : function()
7899     {
7900         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7901         
7902         cfg = {
7903             tag: 'table',
7904             cls : 'table',
7905             cn : []
7906         };
7907         if (this.scrollBody) {
7908             cfg.cls += ' table-body-fixed';
7909         }    
7910         if (this.striped) {
7911             cfg.cls += ' table-striped';
7912         }
7913         
7914         if (this.hover) {
7915             cfg.cls += ' table-hover';
7916         }
7917         if (this.bordered) {
7918             cfg.cls += ' table-bordered';
7919         }
7920         if (this.condensed) {
7921             cfg.cls += ' table-condensed';
7922         }
7923         if (this.responsive) {
7924             cfg.cls += ' table-responsive';
7925         }
7926         
7927         if (this.cls) {
7928             cfg.cls+=  ' ' +this.cls;
7929         }
7930         
7931         // this lot should be simplifed...
7932         var _t = this;
7933         var cp = [
7934             'align',
7935             'bgcolor',
7936             'border',
7937             'cellpadding',
7938             'cellspacing',
7939             'frame',
7940             'rules',
7941             'sortable',
7942             'summary',
7943             'width'
7944         ].forEach(function(k) {
7945             if (_t[k]) {
7946                 cfg[k] = _t[k];
7947             }
7948         });
7949         
7950         
7951         if (this.layout) {
7952             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7953         }
7954         
7955         if(this.store || this.cm){
7956             if(this.headerShow){
7957                 cfg.cn.push(this.renderHeader());
7958             }
7959             
7960             cfg.cn.push(this.renderBody());
7961             
7962             if(this.footerShow){
7963                 cfg.cn.push(this.renderFooter());
7964             }
7965             // where does this come from?
7966             //cfg.cls+=  ' TableGrid';
7967         }
7968         
7969         return { cn : [ cfg ] };
7970     },
7971     
7972     initEvents : function()
7973     {   
7974         if(!this.store || !this.cm){
7975             return;
7976         }
7977         if (this.selModel) {
7978             this.selModel.initEvents();
7979         }
7980         
7981         
7982         //Roo.log('initEvents with ds!!!!');
7983         
7984         this.mainBody = this.el.select('tbody', true).first();
7985         this.mainHead = this.el.select('thead', true).first();
7986         this.mainFoot = this.el.select('tfoot', true).first();
7987         
7988         
7989         
7990         var _this = this;
7991         
7992         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7993             e.on('click', _this.sort, _this);
7994         });
7995         
7996         this.mainBody.on("click", this.onClick, this);
7997         this.mainBody.on("dblclick", this.onDblClick, this);
7998         
7999         // why is this done????? = it breaks dialogs??
8000         //this.parent().el.setStyle('position', 'relative');
8001         
8002         
8003         if (this.footer) {
8004             this.footer.parentId = this.id;
8005             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8006             
8007             if(this.lazyLoad){
8008                 this.el.select('tfoot tr td').first().addClass('hide');
8009             }
8010         } 
8011         
8012         if(this.loadMask) {
8013             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8014         }
8015         
8016         this.store.on('load', this.onLoad, this);
8017         this.store.on('beforeload', this.onBeforeLoad, this);
8018         this.store.on('update', this.onUpdate, this);
8019         this.store.on('add', this.onAdd, this);
8020         this.store.on("clear", this.clear, this);
8021         
8022         this.el.on("contextmenu", this.onContextMenu, this);
8023         
8024         this.mainBody.on('scroll', this.onBodyScroll, this);
8025         
8026         this.cm.on("headerchange", this.onHeaderChange, this);
8027         
8028         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8029         
8030     },
8031     
8032     onContextMenu : function(e, t)
8033     {
8034         this.processEvent("contextmenu", e);
8035     },
8036     
8037     processEvent : function(name, e)
8038     {
8039         if (name != 'touchstart' ) {
8040             this.fireEvent(name, e);    
8041         }
8042         
8043         var t = e.getTarget();
8044         
8045         var cell = Roo.get(t);
8046         
8047         if(!cell){
8048             return;
8049         }
8050         
8051         if(cell.findParent('tfoot', false, true)){
8052             return;
8053         }
8054         
8055         if(cell.findParent('thead', false, true)){
8056             
8057             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8058                 cell = Roo.get(t).findParent('th', false, true);
8059                 if (!cell) {
8060                     Roo.log("failed to find th in thead?");
8061                     Roo.log(e.getTarget());
8062                     return;
8063                 }
8064             }
8065             
8066             var cellIndex = cell.dom.cellIndex;
8067             
8068             var ename = name == 'touchstart' ? 'click' : name;
8069             this.fireEvent("header" + ename, this, cellIndex, e);
8070             
8071             return;
8072         }
8073         
8074         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8075             cell = Roo.get(t).findParent('td', false, true);
8076             if (!cell) {
8077                 Roo.log("failed to find th in tbody?");
8078                 Roo.log(e.getTarget());
8079                 return;
8080             }
8081         }
8082         
8083         var row = cell.findParent('tr', false, true);
8084         var cellIndex = cell.dom.cellIndex;
8085         var rowIndex = row.dom.rowIndex - 1;
8086         
8087         if(row !== false){
8088             
8089             this.fireEvent("row" + name, this, rowIndex, e);
8090             
8091             if(cell !== false){
8092             
8093                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8094             }
8095         }
8096         
8097     },
8098     
8099     onMouseover : function(e, el)
8100     {
8101         var cell = Roo.get(el);
8102         
8103         if(!cell){
8104             return;
8105         }
8106         
8107         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8108             cell = cell.findParent('td', false, true);
8109         }
8110         
8111         var row = cell.findParent('tr', false, true);
8112         var cellIndex = cell.dom.cellIndex;
8113         var rowIndex = row.dom.rowIndex - 1; // start from 0
8114         
8115         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8116         
8117     },
8118     
8119     onMouseout : function(e, el)
8120     {
8121         var cell = Roo.get(el);
8122         
8123         if(!cell){
8124             return;
8125         }
8126         
8127         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8128             cell = cell.findParent('td', false, true);
8129         }
8130         
8131         var row = cell.findParent('tr', false, true);
8132         var cellIndex = cell.dom.cellIndex;
8133         var rowIndex = row.dom.rowIndex - 1; // start from 0
8134         
8135         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8136         
8137     },
8138     
8139     onClick : function(e, el)
8140     {
8141         var cell = Roo.get(el);
8142         
8143         if(!cell || (!this.cellSelection && !this.rowSelection)){
8144             return;
8145         }
8146         
8147         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8148             cell = cell.findParent('td', false, true);
8149         }
8150         
8151         if(!cell || typeof(cell) == 'undefined'){
8152             return;
8153         }
8154         
8155         var row = cell.findParent('tr', false, true);
8156         
8157         if(!row || typeof(row) == 'undefined'){
8158             return;
8159         }
8160         
8161         var cellIndex = cell.dom.cellIndex;
8162         var rowIndex = this.getRowIndex(row);
8163         
8164         // why??? - should these not be based on SelectionModel?
8165         if(this.cellSelection){
8166             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8167         }
8168         
8169         if(this.rowSelection){
8170             this.fireEvent('rowclick', this, row, rowIndex, e);
8171         }
8172         
8173         
8174     },
8175         
8176     onDblClick : function(e,el)
8177     {
8178         var cell = Roo.get(el);
8179         
8180         if(!cell || (!this.cellSelection && !this.rowSelection)){
8181             return;
8182         }
8183         
8184         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8185             cell = cell.findParent('td', false, true);
8186         }
8187         
8188         if(!cell || typeof(cell) == 'undefined'){
8189             return;
8190         }
8191         
8192         var row = cell.findParent('tr', false, true);
8193         
8194         if(!row || typeof(row) == 'undefined'){
8195             return;
8196         }
8197         
8198         var cellIndex = cell.dom.cellIndex;
8199         var rowIndex = this.getRowIndex(row);
8200         
8201         if(this.cellSelection){
8202             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8203         }
8204         
8205         if(this.rowSelection){
8206             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8207         }
8208     },
8209     
8210     sort : function(e,el)
8211     {
8212         var col = Roo.get(el);
8213         
8214         if(!col.hasClass('sortable')){
8215             return;
8216         }
8217         
8218         var sort = col.attr('sort');
8219         var dir = 'ASC';
8220         
8221         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8222             dir = 'DESC';
8223         }
8224         
8225         this.store.sortInfo = {field : sort, direction : dir};
8226         
8227         if (this.footer) {
8228             Roo.log("calling footer first");
8229             this.footer.onClick('first');
8230         } else {
8231         
8232             this.store.load({ params : { start : 0 } });
8233         }
8234     },
8235     
8236     renderHeader : function()
8237     {
8238         var header = {
8239             tag: 'thead',
8240             cn : []
8241         };
8242         
8243         var cm = this.cm;
8244         this.totalWidth = 0;
8245         
8246         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8247             
8248             var config = cm.config[i];
8249             
8250             var c = {
8251                 tag: 'th',
8252                 cls : 'x-hcol-' + i,
8253                 style : '',
8254                 html: cm.getColumnHeader(i)
8255             };
8256             
8257             var hh = '';
8258             
8259             if(typeof(config.sortable) != 'undefined' && config.sortable){
8260                 c.cls = 'sortable';
8261                 c.html = '<i class="glyphicon"></i>' + c.html;
8262             }
8263             
8264             // could use BS4 hidden-..-down 
8265             
8266             if(typeof(config.lgHeader) != 'undefined'){
8267                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8268             }
8269             
8270             if(typeof(config.mdHeader) != 'undefined'){
8271                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8272             }
8273             
8274             if(typeof(config.smHeader) != 'undefined'){
8275                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8276             }
8277             
8278             if(typeof(config.xsHeader) != 'undefined'){
8279                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8280             }
8281             
8282             if(hh.length){
8283                 c.html = hh;
8284             }
8285             
8286             if(typeof(config.tooltip) != 'undefined'){
8287                 c.tooltip = config.tooltip;
8288             }
8289             
8290             if(typeof(config.colspan) != 'undefined'){
8291                 c.colspan = config.colspan;
8292             }
8293             
8294             if(typeof(config.hidden) != 'undefined' && config.hidden){
8295                 c.style += ' display:none;';
8296             }
8297             
8298             if(typeof(config.dataIndex) != 'undefined'){
8299                 c.sort = config.dataIndex;
8300             }
8301             
8302            
8303             
8304             if(typeof(config.align) != 'undefined' && config.align.length){
8305                 c.style += ' text-align:' + config.align + ';';
8306             }
8307             
8308             if(typeof(config.width) != 'undefined'){
8309                 c.style += ' width:' + config.width + 'px;';
8310                 this.totalWidth += config.width;
8311             } else {
8312                 this.totalWidth += 100; // assume minimum of 100 per column?
8313             }
8314             
8315             if(typeof(config.cls) != 'undefined'){
8316                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8317             }
8318             
8319             ['xs','sm','md','lg'].map(function(size){
8320                 
8321                 if(typeof(config[size]) == 'undefined'){
8322                     return;
8323                 }
8324                  
8325                 if (!config[size]) { // 0 = hidden
8326                     // BS 4 '0' is treated as hide that column and below.
8327                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8328                     return;
8329                 }
8330                 
8331                 c.cls += ' col-' + size + '-' + config[size] + (
8332                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8333                 );
8334                 
8335                 
8336             });
8337             
8338             header.cn.push(c)
8339         }
8340         
8341         return header;
8342     },
8343     
8344     renderBody : function()
8345     {
8346         var body = {
8347             tag: 'tbody',
8348             cn : [
8349                 {
8350                     tag: 'tr',
8351                     cn : [
8352                         {
8353                             tag : 'td',
8354                             colspan :  this.cm.getColumnCount()
8355                         }
8356                     ]
8357                 }
8358             ]
8359         };
8360         
8361         return body;
8362     },
8363     
8364     renderFooter : function()
8365     {
8366         var footer = {
8367             tag: 'tfoot',
8368             cn : [
8369                 {
8370                     tag: 'tr',
8371                     cn : [
8372                         {
8373                             tag : 'td',
8374                             colspan :  this.cm.getColumnCount()
8375                         }
8376                     ]
8377                 }
8378             ]
8379         };
8380         
8381         return footer;
8382     },
8383     
8384     
8385     
8386     onLoad : function()
8387     {
8388 //        Roo.log('ds onload');
8389         this.clear();
8390         
8391         var _this = this;
8392         var cm = this.cm;
8393         var ds = this.store;
8394         
8395         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8396             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8397             if (_this.store.sortInfo) {
8398                     
8399                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8400                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8401                 }
8402                 
8403                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8404                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8405                 }
8406             }
8407         });
8408         
8409         var tbody =  this.mainBody;
8410               
8411         if(ds.getCount() > 0){
8412             ds.data.each(function(d,rowIndex){
8413                 var row =  this.renderRow(cm, ds, rowIndex);
8414                 
8415                 tbody.createChild(row);
8416                 
8417                 var _this = this;
8418                 
8419                 if(row.cellObjects.length){
8420                     Roo.each(row.cellObjects, function(r){
8421                         _this.renderCellObject(r);
8422                     })
8423                 }
8424                 
8425             }, this);
8426         }
8427         
8428         var tfoot = this.el.select('tfoot', true).first();
8429         
8430         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8431             
8432             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8433             
8434             var total = this.ds.getTotalCount();
8435             
8436             if(this.footer.pageSize < total){
8437                 this.mainFoot.show();
8438             }
8439         }
8440         
8441         Roo.each(this.el.select('tbody td', true).elements, function(e){
8442             e.on('mouseover', _this.onMouseover, _this);
8443         });
8444         
8445         Roo.each(this.el.select('tbody td', true).elements, function(e){
8446             e.on('mouseout', _this.onMouseout, _this);
8447         });
8448         this.fireEvent('rowsrendered', this);
8449         
8450         this.autoSize();
8451     },
8452     
8453     
8454     onUpdate : function(ds,record)
8455     {
8456         this.refreshRow(record);
8457         this.autoSize();
8458     },
8459     
8460     onRemove : function(ds, record, index, isUpdate){
8461         if(isUpdate !== true){
8462             this.fireEvent("beforerowremoved", this, index, record);
8463         }
8464         var bt = this.mainBody.dom;
8465         
8466         var rows = this.el.select('tbody > tr', true).elements;
8467         
8468         if(typeof(rows[index]) != 'undefined'){
8469             bt.removeChild(rows[index].dom);
8470         }
8471         
8472 //        if(bt.rows[index]){
8473 //            bt.removeChild(bt.rows[index]);
8474 //        }
8475         
8476         if(isUpdate !== true){
8477             //this.stripeRows(index);
8478             //this.syncRowHeights(index, index);
8479             //this.layout();
8480             this.fireEvent("rowremoved", this, index, record);
8481         }
8482     },
8483     
8484     onAdd : function(ds, records, rowIndex)
8485     {
8486         //Roo.log('on Add called');
8487         // - note this does not handle multiple adding very well..
8488         var bt = this.mainBody.dom;
8489         for (var i =0 ; i < records.length;i++) {
8490             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8491             //Roo.log(records[i]);
8492             //Roo.log(this.store.getAt(rowIndex+i));
8493             this.insertRow(this.store, rowIndex + i, false);
8494             return;
8495         }
8496         
8497     },
8498     
8499     
8500     refreshRow : function(record){
8501         var ds = this.store, index;
8502         if(typeof record == 'number'){
8503             index = record;
8504             record = ds.getAt(index);
8505         }else{
8506             index = ds.indexOf(record);
8507             if (index < 0) {
8508                 return; // should not happen - but seems to 
8509             }
8510         }
8511         this.insertRow(ds, index, true);
8512         this.autoSize();
8513         this.onRemove(ds, record, index+1, true);
8514         this.autoSize();
8515         //this.syncRowHeights(index, index);
8516         //this.layout();
8517         this.fireEvent("rowupdated", this, index, record);
8518     },
8519     
8520     insertRow : function(dm, rowIndex, isUpdate){
8521         
8522         if(!isUpdate){
8523             this.fireEvent("beforerowsinserted", this, rowIndex);
8524         }
8525             //var s = this.getScrollState();
8526         var row = this.renderRow(this.cm, this.store, rowIndex);
8527         // insert before rowIndex..
8528         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8529         
8530         var _this = this;
8531                 
8532         if(row.cellObjects.length){
8533             Roo.each(row.cellObjects, function(r){
8534                 _this.renderCellObject(r);
8535             })
8536         }
8537             
8538         if(!isUpdate){
8539             this.fireEvent("rowsinserted", this, rowIndex);
8540             //this.syncRowHeights(firstRow, lastRow);
8541             //this.stripeRows(firstRow);
8542             //this.layout();
8543         }
8544         
8545     },
8546     
8547     
8548     getRowDom : function(rowIndex)
8549     {
8550         var rows = this.el.select('tbody > tr', true).elements;
8551         
8552         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8553         
8554     },
8555     // returns the object tree for a tr..
8556   
8557     
8558     renderRow : function(cm, ds, rowIndex) 
8559     {
8560         var d = ds.getAt(rowIndex);
8561         
8562         var row = {
8563             tag : 'tr',
8564             cls : 'x-row-' + rowIndex,
8565             cn : []
8566         };
8567             
8568         var cellObjects = [];
8569         
8570         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8571             var config = cm.config[i];
8572             
8573             var renderer = cm.getRenderer(i);
8574             var value = '';
8575             var id = false;
8576             
8577             if(typeof(renderer) !== 'undefined'){
8578                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8579             }
8580             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8581             // and are rendered into the cells after the row is rendered - using the id for the element.
8582             
8583             if(typeof(value) === 'object'){
8584                 id = Roo.id();
8585                 cellObjects.push({
8586                     container : id,
8587                     cfg : value 
8588                 })
8589             }
8590             
8591             var rowcfg = {
8592                 record: d,
8593                 rowIndex : rowIndex,
8594                 colIndex : i,
8595                 rowClass : ''
8596             };
8597
8598             this.fireEvent('rowclass', this, rowcfg);
8599             
8600             var td = {
8601                 tag: 'td',
8602                 cls : rowcfg.rowClass + ' x-col-' + i,
8603                 style: '',
8604                 html: (typeof(value) === 'object') ? '' : value
8605             };
8606             
8607             if (id) {
8608                 td.id = id;
8609             }
8610             
8611             if(typeof(config.colspan) != 'undefined'){
8612                 td.colspan = config.colspan;
8613             }
8614             
8615             if(typeof(config.hidden) != 'undefined' && config.hidden){
8616                 td.style += ' display:none;';
8617             }
8618             
8619             if(typeof(config.align) != 'undefined' && config.align.length){
8620                 td.style += ' text-align:' + config.align + ';';
8621             }
8622             if(typeof(config.valign) != 'undefined' && config.valign.length){
8623                 td.style += ' vertical-align:' + config.valign + ';';
8624             }
8625             
8626             if(typeof(config.width) != 'undefined'){
8627                 td.style += ' width:' +  config.width + 'px;';
8628             }
8629             
8630             if(typeof(config.cursor) != 'undefined'){
8631                 td.style += ' cursor:' +  config.cursor + ';';
8632             }
8633             
8634             if(typeof(config.cls) != 'undefined'){
8635                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8636             }
8637             
8638             ['xs','sm','md','lg'].map(function(size){
8639                 
8640                 if(typeof(config[size]) == 'undefined'){
8641                     return;
8642                 }
8643                 
8644                 
8645                   
8646                 if (!config[size]) { // 0 = hidden
8647                     // BS 4 '0' is treated as hide that column and below.
8648                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8649                     return;
8650                 }
8651                 
8652                 td.cls += ' col-' + size + '-' + config[size] + (
8653                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8654                 );
8655                  
8656
8657             });
8658             
8659             row.cn.push(td);
8660            
8661         }
8662         
8663         row.cellObjects = cellObjects;
8664         
8665         return row;
8666           
8667     },
8668     
8669     
8670     
8671     onBeforeLoad : function()
8672     {
8673         
8674     },
8675      /**
8676      * Remove all rows
8677      */
8678     clear : function()
8679     {
8680         this.el.select('tbody', true).first().dom.innerHTML = '';
8681     },
8682     /**
8683      * Show or hide a row.
8684      * @param {Number} rowIndex to show or hide
8685      * @param {Boolean} state hide
8686      */
8687     setRowVisibility : function(rowIndex, state)
8688     {
8689         var bt = this.mainBody.dom;
8690         
8691         var rows = this.el.select('tbody > tr', true).elements;
8692         
8693         if(typeof(rows[rowIndex]) == 'undefined'){
8694             return;
8695         }
8696         rows[rowIndex].dom.style.display = state ? '' : 'none';
8697     },
8698     
8699     
8700     getSelectionModel : function(){
8701         if(!this.selModel){
8702             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8703         }
8704         return this.selModel;
8705     },
8706     /*
8707      * Render the Roo.bootstrap object from renderder
8708      */
8709     renderCellObject : function(r)
8710     {
8711         var _this = this;
8712         
8713         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8714         
8715         var t = r.cfg.render(r.container);
8716         
8717         if(r.cfg.cn){
8718             Roo.each(r.cfg.cn, function(c){
8719                 var child = {
8720                     container: t.getChildContainer(),
8721                     cfg: c
8722                 };
8723                 _this.renderCellObject(child);
8724             })
8725         }
8726     },
8727     
8728     getRowIndex : function(row)
8729     {
8730         var rowIndex = -1;
8731         
8732         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8733             if(el != row){
8734                 return;
8735             }
8736             
8737             rowIndex = index;
8738         });
8739         
8740         return rowIndex;
8741     },
8742      /**
8743      * Returns the grid's underlying element = used by panel.Grid
8744      * @return {Element} The element
8745      */
8746     getGridEl : function(){
8747         return this.el;
8748     },
8749      /**
8750      * Forces a resize - used by panel.Grid
8751      * @return {Element} The element
8752      */
8753     autoSize : function()
8754     {
8755         //var ctr = Roo.get(this.container.dom.parentElement);
8756         var ctr = Roo.get(this.el.dom);
8757         
8758         var thd = this.getGridEl().select('thead',true).first();
8759         var tbd = this.getGridEl().select('tbody', true).first();
8760         var tfd = this.getGridEl().select('tfoot', true).first();
8761         
8762         var cw = ctr.getWidth();
8763         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8764         
8765         if (tbd) {
8766             
8767             tbd.setWidth(ctr.getWidth());
8768             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8769             // this needs fixing for various usage - currently only hydra job advers I think..
8770             //tdb.setHeight(
8771             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8772             //); 
8773             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8774             cw -= barsize;
8775         }
8776         cw = Math.max(cw, this.totalWidth);
8777         this.getGridEl().select('tbody tr',true).setWidth(cw);
8778         
8779         // resize 'expandable coloumn?
8780         
8781         return; // we doe not have a view in this design..
8782         
8783     },
8784     onBodyScroll: function()
8785     {
8786         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8787         if(this.mainHead){
8788             this.mainHead.setStyle({
8789                 'position' : 'relative',
8790                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8791             });
8792         }
8793         
8794         if(this.lazyLoad){
8795             
8796             var scrollHeight = this.mainBody.dom.scrollHeight;
8797             
8798             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8799             
8800             var height = this.mainBody.getHeight();
8801             
8802             if(scrollHeight - height == scrollTop) {
8803                 
8804                 var total = this.ds.getTotalCount();
8805                 
8806                 if(this.footer.cursor + this.footer.pageSize < total){
8807                     
8808                     this.footer.ds.load({
8809                         params : {
8810                             start : this.footer.cursor + this.footer.pageSize,
8811                             limit : this.footer.pageSize
8812                         },
8813                         add : true
8814                     });
8815                 }
8816             }
8817             
8818         }
8819     },
8820     
8821     onHeaderChange : function()
8822     {
8823         var header = this.renderHeader();
8824         var table = this.el.select('table', true).first();
8825         
8826         this.mainHead.remove();
8827         this.mainHead = table.createChild(header, this.mainBody, false);
8828     },
8829     
8830     onHiddenChange : function(colModel, colIndex, hidden)
8831     {
8832         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8833         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8834         
8835         this.CSS.updateRule(thSelector, "display", "");
8836         this.CSS.updateRule(tdSelector, "display", "");
8837         
8838         if(hidden){
8839             this.CSS.updateRule(thSelector, "display", "none");
8840             this.CSS.updateRule(tdSelector, "display", "none");
8841         }
8842         
8843         this.onHeaderChange();
8844         this.onLoad();
8845     },
8846     
8847     setColumnWidth: function(col_index, width)
8848     {
8849         // width = "md-2 xs-2..."
8850         if(!this.colModel.config[col_index]) {
8851             return;
8852         }
8853         
8854         var w = width.split(" ");
8855         
8856         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8857         
8858         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8859         
8860         
8861         for(var j = 0; j < w.length; j++) {
8862             
8863             if(!w[j]) {
8864                 continue;
8865             }
8866             
8867             var size_cls = w[j].split("-");
8868             
8869             if(!Number.isInteger(size_cls[1] * 1)) {
8870                 continue;
8871             }
8872             
8873             if(!this.colModel.config[col_index][size_cls[0]]) {
8874                 continue;
8875             }
8876             
8877             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8878                 continue;
8879             }
8880             
8881             h_row[0].classList.replace(
8882                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8883                 "col-"+size_cls[0]+"-"+size_cls[1]
8884             );
8885             
8886             for(var i = 0; i < rows.length; i++) {
8887                 
8888                 var size_cls = w[j].split("-");
8889                 
8890                 if(!Number.isInteger(size_cls[1] * 1)) {
8891                     continue;
8892                 }
8893                 
8894                 if(!this.colModel.config[col_index][size_cls[0]]) {
8895                     continue;
8896                 }
8897                 
8898                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8899                     continue;
8900                 }
8901                 
8902                 rows[i].classList.replace(
8903                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8904                     "col-"+size_cls[0]+"-"+size_cls[1]
8905                 );
8906             }
8907             
8908             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8909         }
8910     }
8911 });
8912
8913  
8914
8915  /*
8916  * - LGPL
8917  *
8918  * table cell
8919  * 
8920  */
8921
8922 /**
8923  * @class Roo.bootstrap.TableCell
8924  * @extends Roo.bootstrap.Component
8925  * Bootstrap TableCell class
8926  * @cfg {String} html cell contain text
8927  * @cfg {String} cls cell class
8928  * @cfg {String} tag cell tag (td|th) default td
8929  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8930  * @cfg {String} align Aligns the content in a cell
8931  * @cfg {String} axis Categorizes cells
8932  * @cfg {String} bgcolor Specifies the background color of a cell
8933  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8934  * @cfg {Number} colspan Specifies the number of columns a cell should span
8935  * @cfg {String} headers Specifies one or more header cells a cell is related to
8936  * @cfg {Number} height Sets the height of a cell
8937  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8938  * @cfg {Number} rowspan Sets the number of rows a cell should span
8939  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8940  * @cfg {String} valign Vertical aligns the content in a cell
8941  * @cfg {Number} width Specifies the width of a cell
8942  * 
8943  * @constructor
8944  * Create a new TableCell
8945  * @param {Object} config The config object
8946  */
8947
8948 Roo.bootstrap.TableCell = function(config){
8949     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8950 };
8951
8952 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8953     
8954     html: false,
8955     cls: false,
8956     tag: false,
8957     abbr: false,
8958     align: false,
8959     axis: false,
8960     bgcolor: false,
8961     charoff: false,
8962     colspan: false,
8963     headers: false,
8964     height: false,
8965     nowrap: false,
8966     rowspan: false,
8967     scope: false,
8968     valign: false,
8969     width: false,
8970     
8971     
8972     getAutoCreate : function(){
8973         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8974         
8975         cfg = {
8976             tag: 'td'
8977         };
8978         
8979         if(this.tag){
8980             cfg.tag = this.tag;
8981         }
8982         
8983         if (this.html) {
8984             cfg.html=this.html
8985         }
8986         if (this.cls) {
8987             cfg.cls=this.cls
8988         }
8989         if (this.abbr) {
8990             cfg.abbr=this.abbr
8991         }
8992         if (this.align) {
8993             cfg.align=this.align
8994         }
8995         if (this.axis) {
8996             cfg.axis=this.axis
8997         }
8998         if (this.bgcolor) {
8999             cfg.bgcolor=this.bgcolor
9000         }
9001         if (this.charoff) {
9002             cfg.charoff=this.charoff
9003         }
9004         if (this.colspan) {
9005             cfg.colspan=this.colspan
9006         }
9007         if (this.headers) {
9008             cfg.headers=this.headers
9009         }
9010         if (this.height) {
9011             cfg.height=this.height
9012         }
9013         if (this.nowrap) {
9014             cfg.nowrap=this.nowrap
9015         }
9016         if (this.rowspan) {
9017             cfg.rowspan=this.rowspan
9018         }
9019         if (this.scope) {
9020             cfg.scope=this.scope
9021         }
9022         if (this.valign) {
9023             cfg.valign=this.valign
9024         }
9025         if (this.width) {
9026             cfg.width=this.width
9027         }
9028         
9029         
9030         return cfg;
9031     }
9032    
9033 });
9034
9035  
9036
9037  /*
9038  * - LGPL
9039  *
9040  * table row
9041  * 
9042  */
9043
9044 /**
9045  * @class Roo.bootstrap.TableRow
9046  * @extends Roo.bootstrap.Component
9047  * Bootstrap TableRow class
9048  * @cfg {String} cls row class
9049  * @cfg {String} align Aligns the content in a table row
9050  * @cfg {String} bgcolor Specifies a background color for a table row
9051  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9052  * @cfg {String} valign Vertical aligns the content in a table row
9053  * 
9054  * @constructor
9055  * Create a new TableRow
9056  * @param {Object} config The config object
9057  */
9058
9059 Roo.bootstrap.TableRow = function(config){
9060     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9061 };
9062
9063 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9064     
9065     cls: false,
9066     align: false,
9067     bgcolor: false,
9068     charoff: false,
9069     valign: false,
9070     
9071     getAutoCreate : function(){
9072         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9073         
9074         cfg = {
9075             tag: 'tr'
9076         };
9077             
9078         if(this.cls){
9079             cfg.cls = this.cls;
9080         }
9081         if(this.align){
9082             cfg.align = this.align;
9083         }
9084         if(this.bgcolor){
9085             cfg.bgcolor = this.bgcolor;
9086         }
9087         if(this.charoff){
9088             cfg.charoff = this.charoff;
9089         }
9090         if(this.valign){
9091             cfg.valign = this.valign;
9092         }
9093         
9094         return cfg;
9095     }
9096    
9097 });
9098
9099  
9100
9101  /*
9102  * - LGPL
9103  *
9104  * table body
9105  * 
9106  */
9107
9108 /**
9109  * @class Roo.bootstrap.TableBody
9110  * @extends Roo.bootstrap.Component
9111  * Bootstrap TableBody class
9112  * @cfg {String} cls element class
9113  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9114  * @cfg {String} align Aligns the content inside the element
9115  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9116  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9117  * 
9118  * @constructor
9119  * Create a new TableBody
9120  * @param {Object} config The config object
9121  */
9122
9123 Roo.bootstrap.TableBody = function(config){
9124     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9125 };
9126
9127 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9128     
9129     cls: false,
9130     tag: false,
9131     align: false,
9132     charoff: false,
9133     valign: false,
9134     
9135     getAutoCreate : function(){
9136         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9137         
9138         cfg = {
9139             tag: 'tbody'
9140         };
9141             
9142         if (this.cls) {
9143             cfg.cls=this.cls
9144         }
9145         if(this.tag){
9146             cfg.tag = this.tag;
9147         }
9148         
9149         if(this.align){
9150             cfg.align = this.align;
9151         }
9152         if(this.charoff){
9153             cfg.charoff = this.charoff;
9154         }
9155         if(this.valign){
9156             cfg.valign = this.valign;
9157         }
9158         
9159         return cfg;
9160     }
9161     
9162     
9163 //    initEvents : function()
9164 //    {
9165 //        
9166 //        if(!this.store){
9167 //            return;
9168 //        }
9169 //        
9170 //        this.store = Roo.factory(this.store, Roo.data);
9171 //        this.store.on('load', this.onLoad, this);
9172 //        
9173 //        this.store.load();
9174 //        
9175 //    },
9176 //    
9177 //    onLoad: function () 
9178 //    {   
9179 //        this.fireEvent('load', this);
9180 //    }
9181 //    
9182 //   
9183 });
9184
9185  
9186
9187  /*
9188  * Based on:
9189  * Ext JS Library 1.1.1
9190  * Copyright(c) 2006-2007, Ext JS, LLC.
9191  *
9192  * Originally Released Under LGPL - original licence link has changed is not relivant.
9193  *
9194  * Fork - LGPL
9195  * <script type="text/javascript">
9196  */
9197
9198 // as we use this in bootstrap.
9199 Roo.namespace('Roo.form');
9200  /**
9201  * @class Roo.form.Action
9202  * Internal Class used to handle form actions
9203  * @constructor
9204  * @param {Roo.form.BasicForm} el The form element or its id
9205  * @param {Object} config Configuration options
9206  */
9207
9208  
9209  
9210 // define the action interface
9211 Roo.form.Action = function(form, options){
9212     this.form = form;
9213     this.options = options || {};
9214 };
9215 /**
9216  * Client Validation Failed
9217  * @const 
9218  */
9219 Roo.form.Action.CLIENT_INVALID = 'client';
9220 /**
9221  * Server Validation Failed
9222  * @const 
9223  */
9224 Roo.form.Action.SERVER_INVALID = 'server';
9225  /**
9226  * Connect to Server Failed
9227  * @const 
9228  */
9229 Roo.form.Action.CONNECT_FAILURE = 'connect';
9230 /**
9231  * Reading Data from Server Failed
9232  * @const 
9233  */
9234 Roo.form.Action.LOAD_FAILURE = 'load';
9235
9236 Roo.form.Action.prototype = {
9237     type : 'default',
9238     failureType : undefined,
9239     response : undefined,
9240     result : undefined,
9241
9242     // interface method
9243     run : function(options){
9244
9245     },
9246
9247     // interface method
9248     success : function(response){
9249
9250     },
9251
9252     // interface method
9253     handleResponse : function(response){
9254
9255     },
9256
9257     // default connection failure
9258     failure : function(response){
9259         
9260         this.response = response;
9261         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9262         this.form.afterAction(this, false);
9263     },
9264
9265     processResponse : function(response){
9266         this.response = response;
9267         if(!response.responseText){
9268             return true;
9269         }
9270         this.result = this.handleResponse(response);
9271         return this.result;
9272     },
9273
9274     // utility functions used internally
9275     getUrl : function(appendParams){
9276         var url = this.options.url || this.form.url || this.form.el.dom.action;
9277         if(appendParams){
9278             var p = this.getParams();
9279             if(p){
9280                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9281             }
9282         }
9283         return url;
9284     },
9285
9286     getMethod : function(){
9287         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9288     },
9289
9290     getParams : function(){
9291         var bp = this.form.baseParams;
9292         var p = this.options.params;
9293         if(p){
9294             if(typeof p == "object"){
9295                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9296             }else if(typeof p == 'string' && bp){
9297                 p += '&' + Roo.urlEncode(bp);
9298             }
9299         }else if(bp){
9300             p = Roo.urlEncode(bp);
9301         }
9302         return p;
9303     },
9304
9305     createCallback : function(){
9306         return {
9307             success: this.success,
9308             failure: this.failure,
9309             scope: this,
9310             timeout: (this.form.timeout*1000),
9311             upload: this.form.fileUpload ? this.success : undefined
9312         };
9313     }
9314 };
9315
9316 Roo.form.Action.Submit = function(form, options){
9317     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9318 };
9319
9320 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9321     type : 'submit',
9322
9323     haveProgress : false,
9324     uploadComplete : false,
9325     
9326     // uploadProgress indicator.
9327     uploadProgress : function()
9328     {
9329         if (!this.form.progressUrl) {
9330             return;
9331         }
9332         
9333         if (!this.haveProgress) {
9334             Roo.MessageBox.progress("Uploading", "Uploading");
9335         }
9336         if (this.uploadComplete) {
9337            Roo.MessageBox.hide();
9338            return;
9339         }
9340         
9341         this.haveProgress = true;
9342    
9343         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9344         
9345         var c = new Roo.data.Connection();
9346         c.request({
9347             url : this.form.progressUrl,
9348             params: {
9349                 id : uid
9350             },
9351             method: 'GET',
9352             success : function(req){
9353                //console.log(data);
9354                 var rdata = false;
9355                 var edata;
9356                 try  {
9357                    rdata = Roo.decode(req.responseText)
9358                 } catch (e) {
9359                     Roo.log("Invalid data from server..");
9360                     Roo.log(edata);
9361                     return;
9362                 }
9363                 if (!rdata || !rdata.success) {
9364                     Roo.log(rdata);
9365                     Roo.MessageBox.alert(Roo.encode(rdata));
9366                     return;
9367                 }
9368                 var data = rdata.data;
9369                 
9370                 if (this.uploadComplete) {
9371                    Roo.MessageBox.hide();
9372                    return;
9373                 }
9374                    
9375                 if (data){
9376                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9377                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9378                     );
9379                 }
9380                 this.uploadProgress.defer(2000,this);
9381             },
9382        
9383             failure: function(data) {
9384                 Roo.log('progress url failed ');
9385                 Roo.log(data);
9386             },
9387             scope : this
9388         });
9389            
9390     },
9391     
9392     
9393     run : function()
9394     {
9395         // run get Values on the form, so it syncs any secondary forms.
9396         this.form.getValues();
9397         
9398         var o = this.options;
9399         var method = this.getMethod();
9400         var isPost = method == 'POST';
9401         if(o.clientValidation === false || this.form.isValid()){
9402             
9403             if (this.form.progressUrl) {
9404                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9405                     (new Date() * 1) + '' + Math.random());
9406                     
9407             } 
9408             
9409             
9410             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9411                 form:this.form.el.dom,
9412                 url:this.getUrl(!isPost),
9413                 method: method,
9414                 params:isPost ? this.getParams() : null,
9415                 isUpload: this.form.fileUpload,
9416                 formData : this.form.formData
9417             }));
9418             
9419             this.uploadProgress();
9420
9421         }else if (o.clientValidation !== false){ // client validation failed
9422             this.failureType = Roo.form.Action.CLIENT_INVALID;
9423             this.form.afterAction(this, false);
9424         }
9425     },
9426
9427     success : function(response)
9428     {
9429         this.uploadComplete= true;
9430         if (this.haveProgress) {
9431             Roo.MessageBox.hide();
9432         }
9433         
9434         
9435         var result = this.processResponse(response);
9436         if(result === true || result.success){
9437             this.form.afterAction(this, true);
9438             return;
9439         }
9440         if(result.errors){
9441             this.form.markInvalid(result.errors);
9442             this.failureType = Roo.form.Action.SERVER_INVALID;
9443         }
9444         this.form.afterAction(this, false);
9445     },
9446     failure : function(response)
9447     {
9448         this.uploadComplete= true;
9449         if (this.haveProgress) {
9450             Roo.MessageBox.hide();
9451         }
9452         
9453         this.response = response;
9454         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9455         this.form.afterAction(this, false);
9456     },
9457     
9458     handleResponse : function(response){
9459         if(this.form.errorReader){
9460             var rs = this.form.errorReader.read(response);
9461             var errors = [];
9462             if(rs.records){
9463                 for(var i = 0, len = rs.records.length; i < len; i++) {
9464                     var r = rs.records[i];
9465                     errors[i] = r.data;
9466                 }
9467             }
9468             if(errors.length < 1){
9469                 errors = null;
9470             }
9471             return {
9472                 success : rs.success,
9473                 errors : errors
9474             };
9475         }
9476         var ret = false;
9477         try {
9478             ret = Roo.decode(response.responseText);
9479         } catch (e) {
9480             ret = {
9481                 success: false,
9482                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9483                 errors : []
9484             };
9485         }
9486         return ret;
9487         
9488     }
9489 });
9490
9491
9492 Roo.form.Action.Load = function(form, options){
9493     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9494     this.reader = this.form.reader;
9495 };
9496
9497 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9498     type : 'load',
9499
9500     run : function(){
9501         
9502         Roo.Ajax.request(Roo.apply(
9503                 this.createCallback(), {
9504                     method:this.getMethod(),
9505                     url:this.getUrl(false),
9506                     params:this.getParams()
9507         }));
9508     },
9509
9510     success : function(response){
9511         
9512         var result = this.processResponse(response);
9513         if(result === true || !result.success || !result.data){
9514             this.failureType = Roo.form.Action.LOAD_FAILURE;
9515             this.form.afterAction(this, false);
9516             return;
9517         }
9518         this.form.clearInvalid();
9519         this.form.setValues(result.data);
9520         this.form.afterAction(this, true);
9521     },
9522
9523     handleResponse : function(response){
9524         if(this.form.reader){
9525             var rs = this.form.reader.read(response);
9526             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9527             return {
9528                 success : rs.success,
9529                 data : data
9530             };
9531         }
9532         return Roo.decode(response.responseText);
9533     }
9534 });
9535
9536 Roo.form.Action.ACTION_TYPES = {
9537     'load' : Roo.form.Action.Load,
9538     'submit' : Roo.form.Action.Submit
9539 };/*
9540  * - LGPL
9541  *
9542  * form
9543  *
9544  */
9545
9546 /**
9547  * @class Roo.bootstrap.Form
9548  * @extends Roo.bootstrap.Component
9549  * Bootstrap Form class
9550  * @cfg {String} method  GET | POST (default POST)
9551  * @cfg {String} labelAlign top | left (default top)
9552  * @cfg {String} align left  | right - for navbars
9553  * @cfg {Boolean} loadMask load mask when submit (default true)
9554
9555  *
9556  * @constructor
9557  * Create a new Form
9558  * @param {Object} config The config object
9559  */
9560
9561
9562 Roo.bootstrap.Form = function(config){
9563     
9564     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9565     
9566     Roo.bootstrap.Form.popover.apply();
9567     
9568     this.addEvents({
9569         /**
9570          * @event clientvalidation
9571          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9572          * @param {Form} this
9573          * @param {Boolean} valid true if the form has passed client-side validation
9574          */
9575         clientvalidation: true,
9576         /**
9577          * @event beforeaction
9578          * Fires before any action is performed. Return false to cancel the action.
9579          * @param {Form} this
9580          * @param {Action} action The action to be performed
9581          */
9582         beforeaction: true,
9583         /**
9584          * @event actionfailed
9585          * Fires when an action fails.
9586          * @param {Form} this
9587          * @param {Action} action The action that failed
9588          */
9589         actionfailed : true,
9590         /**
9591          * @event actioncomplete
9592          * Fires when an action is completed.
9593          * @param {Form} this
9594          * @param {Action} action The action that completed
9595          */
9596         actioncomplete : true
9597     });
9598 };
9599
9600 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9601
9602      /**
9603      * @cfg {String} method
9604      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9605      */
9606     method : 'POST',
9607     /**
9608      * @cfg {String} url
9609      * The URL to use for form actions if one isn't supplied in the action options.
9610      */
9611     /**
9612      * @cfg {Boolean} fileUpload
9613      * Set to true if this form is a file upload.
9614      */
9615
9616     /**
9617      * @cfg {Object} baseParams
9618      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9619      */
9620
9621     /**
9622      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9623      */
9624     timeout: 30,
9625     /**
9626      * @cfg {Sting} align (left|right) for navbar forms
9627      */
9628     align : 'left',
9629
9630     // private
9631     activeAction : null,
9632
9633     /**
9634      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9635      * element by passing it or its id or mask the form itself by passing in true.
9636      * @type Mixed
9637      */
9638     waitMsgTarget : false,
9639
9640     loadMask : true,
9641     
9642     /**
9643      * @cfg {Boolean} errorMask (true|false) default false
9644      */
9645     errorMask : false,
9646     
9647     /**
9648      * @cfg {Number} maskOffset Default 100
9649      */
9650     maskOffset : 100,
9651     
9652     /**
9653      * @cfg {Boolean} maskBody
9654      */
9655     maskBody : false,
9656
9657     getAutoCreate : function(){
9658
9659         var cfg = {
9660             tag: 'form',
9661             method : this.method || 'POST',
9662             id : this.id || Roo.id(),
9663             cls : ''
9664         };
9665         if (this.parent().xtype.match(/^Nav/)) {
9666             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9667
9668         }
9669
9670         if (this.labelAlign == 'left' ) {
9671             cfg.cls += ' form-horizontal';
9672         }
9673
9674
9675         return cfg;
9676     },
9677     initEvents : function()
9678     {
9679         this.el.on('submit', this.onSubmit, this);
9680         // this was added as random key presses on the form where triggering form submit.
9681         this.el.on('keypress', function(e) {
9682             if (e.getCharCode() != 13) {
9683                 return true;
9684             }
9685             // we might need to allow it for textareas.. and some other items.
9686             // check e.getTarget().
9687
9688             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9689                 return true;
9690             }
9691
9692             Roo.log("keypress blocked");
9693
9694             e.preventDefault();
9695             return false;
9696         });
9697         
9698     },
9699     // private
9700     onSubmit : function(e){
9701         e.stopEvent();
9702     },
9703
9704      /**
9705      * Returns true if client-side validation on the form is successful.
9706      * @return Boolean
9707      */
9708     isValid : function(){
9709         var items = this.getItems();
9710         var valid = true;
9711         var target = false;
9712         
9713         items.each(function(f){
9714             
9715             if(f.validate()){
9716                 return;
9717             }
9718             
9719             Roo.log('invalid field: ' + f.name);
9720             
9721             valid = false;
9722
9723             if(!target && f.el.isVisible(true)){
9724                 target = f;
9725             }
9726            
9727         });
9728         
9729         if(this.errorMask && !valid){
9730             Roo.bootstrap.Form.popover.mask(this, target);
9731         }
9732         
9733         return valid;
9734     },
9735     
9736     /**
9737      * Returns true if any fields in this form have changed since their original load.
9738      * @return Boolean
9739      */
9740     isDirty : function(){
9741         var dirty = false;
9742         var items = this.getItems();
9743         items.each(function(f){
9744            if(f.isDirty()){
9745                dirty = true;
9746                return false;
9747            }
9748            return true;
9749         });
9750         return dirty;
9751     },
9752      /**
9753      * Performs a predefined action (submit or load) or custom actions you define on this form.
9754      * @param {String} actionName The name of the action type
9755      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9756      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9757      * accept other config options):
9758      * <pre>
9759 Property          Type             Description
9760 ----------------  ---------------  ----------------------------------------------------------------------------------
9761 url               String           The url for the action (defaults to the form's url)
9762 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9763 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9764 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9765                                    validate the form on the client (defaults to false)
9766      * </pre>
9767      * @return {BasicForm} this
9768      */
9769     doAction : function(action, options){
9770         if(typeof action == 'string'){
9771             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9772         }
9773         if(this.fireEvent('beforeaction', this, action) !== false){
9774             this.beforeAction(action);
9775             action.run.defer(100, action);
9776         }
9777         return this;
9778     },
9779
9780     // private
9781     beforeAction : function(action){
9782         var o = action.options;
9783         
9784         if(this.loadMask){
9785             
9786             if(this.maskBody){
9787                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9788             } else {
9789                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9790             }
9791         }
9792         // not really supported yet.. ??
9793
9794         //if(this.waitMsgTarget === true){
9795         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9796         //}else if(this.waitMsgTarget){
9797         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9798         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9799         //}else {
9800         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9801        // }
9802
9803     },
9804
9805     // private
9806     afterAction : function(action, success){
9807         this.activeAction = null;
9808         var o = action.options;
9809
9810         if(this.loadMask){
9811             
9812             if(this.maskBody){
9813                 Roo.get(document.body).unmask();
9814             } else {
9815                 this.el.unmask();
9816             }
9817         }
9818         
9819         //if(this.waitMsgTarget === true){
9820 //            this.el.unmask();
9821         //}else if(this.waitMsgTarget){
9822         //    this.waitMsgTarget.unmask();
9823         //}else{
9824         //    Roo.MessageBox.updateProgress(1);
9825         //    Roo.MessageBox.hide();
9826        // }
9827         //
9828         if(success){
9829             if(o.reset){
9830                 this.reset();
9831             }
9832             Roo.callback(o.success, o.scope, [this, action]);
9833             this.fireEvent('actioncomplete', this, action);
9834
9835         }else{
9836
9837             // failure condition..
9838             // we have a scenario where updates need confirming.
9839             // eg. if a locking scenario exists..
9840             // we look for { errors : { needs_confirm : true }} in the response.
9841             if (
9842                 (typeof(action.result) != 'undefined')  &&
9843                 (typeof(action.result.errors) != 'undefined')  &&
9844                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9845            ){
9846                 var _t = this;
9847                 Roo.log("not supported yet");
9848                  /*
9849
9850                 Roo.MessageBox.confirm(
9851                     "Change requires confirmation",
9852                     action.result.errorMsg,
9853                     function(r) {
9854                         if (r != 'yes') {
9855                             return;
9856                         }
9857                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9858                     }
9859
9860                 );
9861                 */
9862
9863
9864                 return;
9865             }
9866
9867             Roo.callback(o.failure, o.scope, [this, action]);
9868             // show an error message if no failed handler is set..
9869             if (!this.hasListener('actionfailed')) {
9870                 Roo.log("need to add dialog support");
9871                 /*
9872                 Roo.MessageBox.alert("Error",
9873                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9874                         action.result.errorMsg :
9875                         "Saving Failed, please check your entries or try again"
9876                 );
9877                 */
9878             }
9879
9880             this.fireEvent('actionfailed', this, action);
9881         }
9882
9883     },
9884     /**
9885      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9886      * @param {String} id The value to search for
9887      * @return Field
9888      */
9889     findField : function(id){
9890         var items = this.getItems();
9891         var field = items.get(id);
9892         if(!field){
9893              items.each(function(f){
9894                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9895                     field = f;
9896                     return false;
9897                 }
9898                 return true;
9899             });
9900         }
9901         return field || null;
9902     },
9903      /**
9904      * Mark fields in this form invalid in bulk.
9905      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9906      * @return {BasicForm} this
9907      */
9908     markInvalid : function(errors){
9909         if(errors instanceof Array){
9910             for(var i = 0, len = errors.length; i < len; i++){
9911                 var fieldError = errors[i];
9912                 var f = this.findField(fieldError.id);
9913                 if(f){
9914                     f.markInvalid(fieldError.msg);
9915                 }
9916             }
9917         }else{
9918             var field, id;
9919             for(id in errors){
9920                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9921                     field.markInvalid(errors[id]);
9922                 }
9923             }
9924         }
9925         //Roo.each(this.childForms || [], function (f) {
9926         //    f.markInvalid(errors);
9927         //});
9928
9929         return this;
9930     },
9931
9932     /**
9933      * Set values for fields in this form in bulk.
9934      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9935      * @return {BasicForm} this
9936      */
9937     setValues : function(values){
9938         if(values instanceof Array){ // array of objects
9939             for(var i = 0, len = values.length; i < len; i++){
9940                 var v = values[i];
9941                 var f = this.findField(v.id);
9942                 if(f){
9943                     f.setValue(v.value);
9944                     if(this.trackResetOnLoad){
9945                         f.originalValue = f.getValue();
9946                     }
9947                 }
9948             }
9949         }else{ // object hash
9950             var field, id;
9951             for(id in values){
9952                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9953
9954                     if (field.setFromData &&
9955                         field.valueField &&
9956                         field.displayField &&
9957                         // combos' with local stores can
9958                         // be queried via setValue()
9959                         // to set their value..
9960                         (field.store && !field.store.isLocal)
9961                         ) {
9962                         // it's a combo
9963                         var sd = { };
9964                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9965                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9966                         field.setFromData(sd);
9967
9968                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9969                         
9970                         field.setFromData(values);
9971                         
9972                     } else {
9973                         field.setValue(values[id]);
9974                     }
9975
9976
9977                     if(this.trackResetOnLoad){
9978                         field.originalValue = field.getValue();
9979                     }
9980                 }
9981             }
9982         }
9983
9984         //Roo.each(this.childForms || [], function (f) {
9985         //    f.setValues(values);
9986         //});
9987
9988         return this;
9989     },
9990
9991     /**
9992      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9993      * they are returned as an array.
9994      * @param {Boolean} asString
9995      * @return {Object}
9996      */
9997     getValues : function(asString){
9998         //if (this.childForms) {
9999             // copy values from the child forms
10000         //    Roo.each(this.childForms, function (f) {
10001         //        this.setValues(f.getValues());
10002         //    }, this);
10003         //}
10004
10005
10006
10007         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10008         if(asString === true){
10009             return fs;
10010         }
10011         return Roo.urlDecode(fs);
10012     },
10013
10014     /**
10015      * Returns the fields in this form as an object with key/value pairs.
10016      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10017      * @return {Object}
10018      */
10019     getFieldValues : function(with_hidden)
10020     {
10021         var items = this.getItems();
10022         var ret = {};
10023         items.each(function(f){
10024             
10025             if (!f.getName()) {
10026                 return;
10027             }
10028             
10029             var v = f.getValue();
10030             
10031             if (f.inputType =='radio') {
10032                 if (typeof(ret[f.getName()]) == 'undefined') {
10033                     ret[f.getName()] = ''; // empty..
10034                 }
10035
10036                 if (!f.el.dom.checked) {
10037                     return;
10038
10039                 }
10040                 v = f.el.dom.value;
10041
10042             }
10043             
10044             if(f.xtype == 'MoneyField'){
10045                 ret[f.currencyName] = f.getCurrency();
10046             }
10047
10048             // not sure if this supported any more..
10049             if ((typeof(v) == 'object') && f.getRawValue) {
10050                 v = f.getRawValue() ; // dates..
10051             }
10052             // combo boxes where name != hiddenName...
10053             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10054                 ret[f.name] = f.getRawValue();
10055             }
10056             ret[f.getName()] = v;
10057         });
10058
10059         return ret;
10060     },
10061
10062     /**
10063      * Clears all invalid messages in this form.
10064      * @return {BasicForm} this
10065      */
10066     clearInvalid : function(){
10067         var items = this.getItems();
10068
10069         items.each(function(f){
10070            f.clearInvalid();
10071         });
10072
10073         return this;
10074     },
10075
10076     /**
10077      * Resets this form.
10078      * @return {BasicForm} this
10079      */
10080     reset : function(){
10081         var items = this.getItems();
10082         items.each(function(f){
10083             f.reset();
10084         });
10085
10086         Roo.each(this.childForms || [], function (f) {
10087             f.reset();
10088         });
10089
10090
10091         return this;
10092     },
10093     
10094     getItems : function()
10095     {
10096         var r=new Roo.util.MixedCollection(false, function(o){
10097             return o.id || (o.id = Roo.id());
10098         });
10099         var iter = function(el) {
10100             if (el.inputEl) {
10101                 r.add(el);
10102             }
10103             if (!el.items) {
10104                 return;
10105             }
10106             Roo.each(el.items,function(e) {
10107                 iter(e);
10108             });
10109         };
10110
10111         iter(this);
10112         return r;
10113     },
10114     
10115     hideFields : function(items)
10116     {
10117         Roo.each(items, function(i){
10118             
10119             var f = this.findField(i);
10120             
10121             if(!f){
10122                 return;
10123             }
10124             
10125             f.hide();
10126             
10127         }, this);
10128     },
10129     
10130     showFields : function(items)
10131     {
10132         Roo.each(items, function(i){
10133             
10134             var f = this.findField(i);
10135             
10136             if(!f){
10137                 return;
10138             }
10139             
10140             f.show();
10141             
10142         }, this);
10143     }
10144
10145 });
10146
10147 Roo.apply(Roo.bootstrap.Form, {
10148     
10149     popover : {
10150         
10151         padding : 5,
10152         
10153         isApplied : false,
10154         
10155         isMasked : false,
10156         
10157         form : false,
10158         
10159         target : false,
10160         
10161         toolTip : false,
10162         
10163         intervalID : false,
10164         
10165         maskEl : false,
10166         
10167         apply : function()
10168         {
10169             if(this.isApplied){
10170                 return;
10171             }
10172             
10173             this.maskEl = {
10174                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10175                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10176                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10177                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10178             };
10179             
10180             this.maskEl.top.enableDisplayMode("block");
10181             this.maskEl.left.enableDisplayMode("block");
10182             this.maskEl.bottom.enableDisplayMode("block");
10183             this.maskEl.right.enableDisplayMode("block");
10184             
10185             this.toolTip = new Roo.bootstrap.Tooltip({
10186                 cls : 'roo-form-error-popover',
10187                 alignment : {
10188                     'left' : ['r-l', [-2,0], 'right'],
10189                     'right' : ['l-r', [2,0], 'left'],
10190                     'bottom' : ['tl-bl', [0,2], 'top'],
10191                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10192                 }
10193             });
10194             
10195             this.toolTip.render(Roo.get(document.body));
10196
10197             this.toolTip.el.enableDisplayMode("block");
10198             
10199             Roo.get(document.body).on('click', function(){
10200                 this.unmask();
10201             }, this);
10202             
10203             Roo.get(document.body).on('touchstart', function(){
10204                 this.unmask();
10205             }, this);
10206             
10207             this.isApplied = true
10208         },
10209         
10210         mask : function(form, target)
10211         {
10212             this.form = form;
10213             
10214             this.target = target;
10215             
10216             if(!this.form.errorMask || !target.el){
10217                 return;
10218             }
10219             
10220             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10221             
10222             Roo.log(scrollable);
10223             
10224             var ot = this.target.el.calcOffsetsTo(scrollable);
10225             
10226             var scrollTo = ot[1] - this.form.maskOffset;
10227             
10228             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10229             
10230             scrollable.scrollTo('top', scrollTo);
10231             
10232             var box = this.target.el.getBox();
10233             Roo.log(box);
10234             var zIndex = Roo.bootstrap.Modal.zIndex++;
10235
10236             
10237             this.maskEl.top.setStyle('position', 'absolute');
10238             this.maskEl.top.setStyle('z-index', zIndex);
10239             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10240             this.maskEl.top.setLeft(0);
10241             this.maskEl.top.setTop(0);
10242             this.maskEl.top.show();
10243             
10244             this.maskEl.left.setStyle('position', 'absolute');
10245             this.maskEl.left.setStyle('z-index', zIndex);
10246             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10247             this.maskEl.left.setLeft(0);
10248             this.maskEl.left.setTop(box.y - this.padding);
10249             this.maskEl.left.show();
10250
10251             this.maskEl.bottom.setStyle('position', 'absolute');
10252             this.maskEl.bottom.setStyle('z-index', zIndex);
10253             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10254             this.maskEl.bottom.setLeft(0);
10255             this.maskEl.bottom.setTop(box.bottom + this.padding);
10256             this.maskEl.bottom.show();
10257
10258             this.maskEl.right.setStyle('position', 'absolute');
10259             this.maskEl.right.setStyle('z-index', zIndex);
10260             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10261             this.maskEl.right.setLeft(box.right + this.padding);
10262             this.maskEl.right.setTop(box.y - this.padding);
10263             this.maskEl.right.show();
10264
10265             this.toolTip.bindEl = this.target.el;
10266
10267             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10268
10269             var tip = this.target.blankText;
10270
10271             if(this.target.getValue() !== '' ) {
10272                 
10273                 if (this.target.invalidText.length) {
10274                     tip = this.target.invalidText;
10275                 } else if (this.target.regexText.length){
10276                     tip = this.target.regexText;
10277                 }
10278             }
10279
10280             this.toolTip.show(tip);
10281
10282             this.intervalID = window.setInterval(function() {
10283                 Roo.bootstrap.Form.popover.unmask();
10284             }, 10000);
10285
10286             window.onwheel = function(){ return false;};
10287             
10288             (function(){ this.isMasked = true; }).defer(500, this);
10289             
10290         },
10291         
10292         unmask : function()
10293         {
10294             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10295                 return;
10296             }
10297             
10298             this.maskEl.top.setStyle('position', 'absolute');
10299             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10300             this.maskEl.top.hide();
10301
10302             this.maskEl.left.setStyle('position', 'absolute');
10303             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10304             this.maskEl.left.hide();
10305
10306             this.maskEl.bottom.setStyle('position', 'absolute');
10307             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10308             this.maskEl.bottom.hide();
10309
10310             this.maskEl.right.setStyle('position', 'absolute');
10311             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10312             this.maskEl.right.hide();
10313             
10314             this.toolTip.hide();
10315             
10316             this.toolTip.el.hide();
10317             
10318             window.onwheel = function(){ return true;};
10319             
10320             if(this.intervalID){
10321                 window.clearInterval(this.intervalID);
10322                 this.intervalID = false;
10323             }
10324             
10325             this.isMasked = false;
10326             
10327         }
10328         
10329     }
10330     
10331 });
10332
10333 /*
10334  * Based on:
10335  * Ext JS Library 1.1.1
10336  * Copyright(c) 2006-2007, Ext JS, LLC.
10337  *
10338  * Originally Released Under LGPL - original licence link has changed is not relivant.
10339  *
10340  * Fork - LGPL
10341  * <script type="text/javascript">
10342  */
10343 /**
10344  * @class Roo.form.VTypes
10345  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10346  * @singleton
10347  */
10348 Roo.form.VTypes = function(){
10349     // closure these in so they are only created once.
10350     var alpha = /^[a-zA-Z_]+$/;
10351     var alphanum = /^[a-zA-Z0-9_]+$/;
10352     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10353     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10354
10355     // All these messages and functions are configurable
10356     return {
10357         /**
10358          * The function used to validate email addresses
10359          * @param {String} value The email address
10360          */
10361         'email' : function(v){
10362             return email.test(v);
10363         },
10364         /**
10365          * The error text to display when the email validation function returns false
10366          * @type String
10367          */
10368         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10369         /**
10370          * The keystroke filter mask to be applied on email input
10371          * @type RegExp
10372          */
10373         'emailMask' : /[a-z0-9_\.\-@]/i,
10374
10375         /**
10376          * The function used to validate URLs
10377          * @param {String} value The URL
10378          */
10379         'url' : function(v){
10380             return url.test(v);
10381         },
10382         /**
10383          * The error text to display when the url validation function returns false
10384          * @type String
10385          */
10386         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10387         
10388         /**
10389          * The function used to validate alpha values
10390          * @param {String} value The value
10391          */
10392         'alpha' : function(v){
10393             return alpha.test(v);
10394         },
10395         /**
10396          * The error text to display when the alpha validation function returns false
10397          * @type String
10398          */
10399         'alphaText' : 'This field should only contain letters and _',
10400         /**
10401          * The keystroke filter mask to be applied on alpha input
10402          * @type RegExp
10403          */
10404         'alphaMask' : /[a-z_]/i,
10405
10406         /**
10407          * The function used to validate alphanumeric values
10408          * @param {String} value The value
10409          */
10410         'alphanum' : function(v){
10411             return alphanum.test(v);
10412         },
10413         /**
10414          * The error text to display when the alphanumeric validation function returns false
10415          * @type String
10416          */
10417         'alphanumText' : 'This field should only contain letters, numbers and _',
10418         /**
10419          * The keystroke filter mask to be applied on alphanumeric input
10420          * @type RegExp
10421          */
10422         'alphanumMask' : /[a-z0-9_]/i
10423     };
10424 }();/*
10425  * - LGPL
10426  *
10427  * Input
10428  * 
10429  */
10430
10431 /**
10432  * @class Roo.bootstrap.Input
10433  * @extends Roo.bootstrap.Component
10434  * Bootstrap Input class
10435  * @cfg {Boolean} disabled is it disabled
10436  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10437  * @cfg {String} name name of the input
10438  * @cfg {string} fieldLabel - the label associated
10439  * @cfg {string} placeholder - placeholder to put in text.
10440  * @cfg {string}  before - input group add on before
10441  * @cfg {string} after - input group add on after
10442  * @cfg {string} size - (lg|sm) or leave empty..
10443  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10444  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10445  * @cfg {Number} md colspan out of 12 for computer-sized screens
10446  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10447  * @cfg {string} value default value of the input
10448  * @cfg {Number} labelWidth set the width of label 
10449  * @cfg {Number} labellg set the width of label (1-12)
10450  * @cfg {Number} labelmd set the width of label (1-12)
10451  * @cfg {Number} labelsm set the width of label (1-12)
10452  * @cfg {Number} labelxs set the width of label (1-12)
10453  * @cfg {String} labelAlign (top|left)
10454  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10455  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10456  * @cfg {String} indicatorpos (left|right) default left
10457  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10458  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10459  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10460
10461  * @cfg {String} align (left|center|right) Default left
10462  * @cfg {Boolean} forceFeedback (true|false) Default false
10463  * 
10464  * @constructor
10465  * Create a new Input
10466  * @param {Object} config The config object
10467  */
10468
10469 Roo.bootstrap.Input = function(config){
10470     
10471     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10472     
10473     this.addEvents({
10474         /**
10475          * @event focus
10476          * Fires when this field receives input focus.
10477          * @param {Roo.form.Field} this
10478          */
10479         focus : true,
10480         /**
10481          * @event blur
10482          * Fires when this field loses input focus.
10483          * @param {Roo.form.Field} this
10484          */
10485         blur : true,
10486         /**
10487          * @event specialkey
10488          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10489          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10490          * @param {Roo.form.Field} this
10491          * @param {Roo.EventObject} e The event object
10492          */
10493         specialkey : true,
10494         /**
10495          * @event change
10496          * Fires just before the field blurs if the field value has changed.
10497          * @param {Roo.form.Field} this
10498          * @param {Mixed} newValue The new value
10499          * @param {Mixed} oldValue The original value
10500          */
10501         change : true,
10502         /**
10503          * @event invalid
10504          * Fires after the field has been marked as invalid.
10505          * @param {Roo.form.Field} this
10506          * @param {String} msg The validation message
10507          */
10508         invalid : true,
10509         /**
10510          * @event valid
10511          * Fires after the field has been validated with no errors.
10512          * @param {Roo.form.Field} this
10513          */
10514         valid : true,
10515          /**
10516          * @event keyup
10517          * Fires after the key up
10518          * @param {Roo.form.Field} this
10519          * @param {Roo.EventObject}  e The event Object
10520          */
10521         keyup : true,
10522         /**
10523          * @event paste
10524          * Fires after the user pastes into input
10525          * @param {Roo.form.Field} this
10526          * @param {Roo.EventObject}  e The event Object
10527          */
10528         paste : true
10529     });
10530 };
10531
10532 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10533      /**
10534      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10535       automatic validation (defaults to "keyup").
10536      */
10537     validationEvent : "keyup",
10538      /**
10539      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10540      */
10541     validateOnBlur : true,
10542     /**
10543      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10544      */
10545     validationDelay : 250,
10546      /**
10547      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10548      */
10549     focusClass : "x-form-focus",  // not needed???
10550     
10551        
10552     /**
10553      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10554      */
10555     invalidClass : "has-warning",
10556     
10557     /**
10558      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10559      */
10560     validClass : "has-success",
10561     
10562     /**
10563      * @cfg {Boolean} hasFeedback (true|false) default true
10564      */
10565     hasFeedback : true,
10566     
10567     /**
10568      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10569      */
10570     invalidFeedbackClass : "glyphicon-warning-sign",
10571     
10572     /**
10573      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10574      */
10575     validFeedbackClass : "glyphicon-ok",
10576     
10577     /**
10578      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10579      */
10580     selectOnFocus : false,
10581     
10582      /**
10583      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10584      */
10585     maskRe : null,
10586        /**
10587      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10588      */
10589     vtype : null,
10590     
10591       /**
10592      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10593      */
10594     disableKeyFilter : false,
10595     
10596        /**
10597      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10598      */
10599     disabled : false,
10600      /**
10601      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10602      */
10603     allowBlank : true,
10604     /**
10605      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10606      */
10607     blankText : "Please complete this mandatory field",
10608     
10609      /**
10610      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10611      */
10612     minLength : 0,
10613     /**
10614      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10615      */
10616     maxLength : Number.MAX_VALUE,
10617     /**
10618      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10619      */
10620     minLengthText : "The minimum length for this field is {0}",
10621     /**
10622      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10623      */
10624     maxLengthText : "The maximum length for this field is {0}",
10625   
10626     
10627     /**
10628      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10629      * If available, this function will be called only after the basic validators all return true, and will be passed the
10630      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10631      */
10632     validator : null,
10633     /**
10634      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10635      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10636      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10637      */
10638     regex : null,
10639     /**
10640      * @cfg {String} regexText -- Depricated - use Invalid Text
10641      */
10642     regexText : "",
10643     
10644     /**
10645      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10646      */
10647     invalidText : "",
10648     
10649     
10650     
10651     autocomplete: false,
10652     
10653     
10654     fieldLabel : '',
10655     inputType : 'text',
10656     
10657     name : false,
10658     placeholder: false,
10659     before : false,
10660     after : false,
10661     size : false,
10662     hasFocus : false,
10663     preventMark: false,
10664     isFormField : true,
10665     value : '',
10666     labelWidth : 2,
10667     labelAlign : false,
10668     readOnly : false,
10669     align : false,
10670     formatedValue : false,
10671     forceFeedback : false,
10672     
10673     indicatorpos : 'left',
10674     
10675     labellg : 0,
10676     labelmd : 0,
10677     labelsm : 0,
10678     labelxs : 0,
10679     
10680     capture : '',
10681     accept : '',
10682     
10683     parentLabelAlign : function()
10684     {
10685         var parent = this;
10686         while (parent.parent()) {
10687             parent = parent.parent();
10688             if (typeof(parent.labelAlign) !='undefined') {
10689                 return parent.labelAlign;
10690             }
10691         }
10692         return 'left';
10693         
10694     },
10695     
10696     getAutoCreate : function()
10697     {
10698         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10699         
10700         var id = Roo.id();
10701         
10702         var cfg = {};
10703         
10704         if(this.inputType != 'hidden'){
10705             cfg.cls = 'form-group' //input-group
10706         }
10707         
10708         var input =  {
10709             tag: 'input',
10710             id : id,
10711             type : this.inputType,
10712             value : this.value,
10713             cls : 'form-control',
10714             placeholder : this.placeholder || '',
10715             autocomplete : this.autocomplete || 'new-password'
10716         };
10717         if (this.inputType == 'file') {
10718             input.style = 'overflow:hidden'; // why not in CSS?
10719         }
10720         
10721         if(this.capture.length){
10722             input.capture = this.capture;
10723         }
10724         
10725         if(this.accept.length){
10726             input.accept = this.accept + "/*";
10727         }
10728         
10729         if(this.align){
10730             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10731         }
10732         
10733         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10734             input.maxLength = this.maxLength;
10735         }
10736         
10737         if (this.disabled) {
10738             input.disabled=true;
10739         }
10740         
10741         if (this.readOnly) {
10742             input.readonly=true;
10743         }
10744         
10745         if (this.name) {
10746             input.name = this.name;
10747         }
10748         
10749         if (this.size) {
10750             input.cls += ' input-' + this.size;
10751         }
10752         
10753         var settings=this;
10754         ['xs','sm','md','lg'].map(function(size){
10755             if (settings[size]) {
10756                 cfg.cls += ' col-' + size + '-' + settings[size];
10757             }
10758         });
10759         
10760         var inputblock = input;
10761         
10762         var feedback = {
10763             tag: 'span',
10764             cls: 'glyphicon form-control-feedback'
10765         };
10766             
10767         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10768             
10769             inputblock = {
10770                 cls : 'has-feedback',
10771                 cn :  [
10772                     input,
10773                     feedback
10774                 ] 
10775             };  
10776         }
10777         
10778         if (this.before || this.after) {
10779             
10780             inputblock = {
10781                 cls : 'input-group',
10782                 cn :  [] 
10783             };
10784             
10785             if (this.before && typeof(this.before) == 'string') {
10786                 
10787                 inputblock.cn.push({
10788                     tag :'span',
10789                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10790                     html : this.before
10791                 });
10792             }
10793             if (this.before && typeof(this.before) == 'object') {
10794                 this.before = Roo.factory(this.before);
10795                 
10796                 inputblock.cn.push({
10797                     tag :'span',
10798                     cls : 'roo-input-before input-group-prepend   input-group-' +
10799                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10800                 });
10801             }
10802             
10803             inputblock.cn.push(input);
10804             
10805             if (this.after && typeof(this.after) == 'string') {
10806                 inputblock.cn.push({
10807                     tag :'span',
10808                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10809                     html : this.after
10810                 });
10811             }
10812             if (this.after && typeof(this.after) == 'object') {
10813                 this.after = Roo.factory(this.after);
10814                 
10815                 inputblock.cn.push({
10816                     tag :'span',
10817                     cls : 'roo-input-after input-group-append  input-group-' +
10818                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10819                 });
10820             }
10821             
10822             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10823                 inputblock.cls += ' has-feedback';
10824                 inputblock.cn.push(feedback);
10825             }
10826         };
10827         var indicator = {
10828             tag : 'i',
10829             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10830             tooltip : 'This field is required'
10831         };
10832         if (this.allowBlank ) {
10833             indicator.style = this.allowBlank ? ' display:none' : '';
10834         }
10835         if (align ==='left' && this.fieldLabel.length) {
10836             
10837             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10838             
10839             cfg.cn = [
10840                 indicator,
10841                 {
10842                     tag: 'label',
10843                     'for' :  id,
10844                     cls : 'control-label col-form-label',
10845                     html : this.fieldLabel
10846
10847                 },
10848                 {
10849                     cls : "", 
10850                     cn: [
10851                         inputblock
10852                     ]
10853                 }
10854             ];
10855             
10856             var labelCfg = cfg.cn[1];
10857             var contentCfg = cfg.cn[2];
10858             
10859             if(this.indicatorpos == 'right'){
10860                 cfg.cn = [
10861                     {
10862                         tag: 'label',
10863                         'for' :  id,
10864                         cls : 'control-label col-form-label',
10865                         cn : [
10866                             {
10867                                 tag : 'span',
10868                                 html : this.fieldLabel
10869                             },
10870                             indicator
10871                         ]
10872                     },
10873                     {
10874                         cls : "",
10875                         cn: [
10876                             inputblock
10877                         ]
10878                     }
10879
10880                 ];
10881                 
10882                 labelCfg = cfg.cn[0];
10883                 contentCfg = cfg.cn[1];
10884             
10885             }
10886             
10887             if(this.labelWidth > 12){
10888                 labelCfg.style = "width: " + this.labelWidth + 'px';
10889             }
10890             
10891             if(this.labelWidth < 13 && this.labelmd == 0){
10892                 this.labelmd = this.labelWidth;
10893             }
10894             
10895             if(this.labellg > 0){
10896                 labelCfg.cls += ' col-lg-' + this.labellg;
10897                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10898             }
10899             
10900             if(this.labelmd > 0){
10901                 labelCfg.cls += ' col-md-' + this.labelmd;
10902                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10903             }
10904             
10905             if(this.labelsm > 0){
10906                 labelCfg.cls += ' col-sm-' + this.labelsm;
10907                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10908             }
10909             
10910             if(this.labelxs > 0){
10911                 labelCfg.cls += ' col-xs-' + this.labelxs;
10912                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10913             }
10914             
10915             
10916         } else if ( this.fieldLabel.length) {
10917                 
10918             
10919             
10920             cfg.cn = [
10921                 {
10922                     tag : 'i',
10923                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10924                     tooltip : 'This field is required',
10925                     style : this.allowBlank ? ' display:none' : '' 
10926                 },
10927                 {
10928                     tag: 'label',
10929                    //cls : 'input-group-addon',
10930                     html : this.fieldLabel
10931
10932                 },
10933
10934                inputblock
10935
10936            ];
10937            
10938            if(this.indicatorpos == 'right'){
10939        
10940                 cfg.cn = [
10941                     {
10942                         tag: 'label',
10943                        //cls : 'input-group-addon',
10944                         html : this.fieldLabel
10945
10946                     },
10947                     {
10948                         tag : 'i',
10949                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10950                         tooltip : 'This field is required',
10951                         style : this.allowBlank ? ' display:none' : '' 
10952                     },
10953
10954                    inputblock
10955
10956                ];
10957
10958             }
10959
10960         } else {
10961             
10962             cfg.cn = [
10963
10964                     inputblock
10965
10966             ];
10967                 
10968                 
10969         };
10970         
10971         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10972            cfg.cls += ' navbar-form';
10973         }
10974         
10975         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10976             // on BS4 we do this only if not form 
10977             cfg.cls += ' navbar-form';
10978             cfg.tag = 'li';
10979         }
10980         
10981         return cfg;
10982         
10983     },
10984     /**
10985      * return the real input element.
10986      */
10987     inputEl: function ()
10988     {
10989         return this.el.select('input.form-control',true).first();
10990     },
10991     
10992     tooltipEl : function()
10993     {
10994         return this.inputEl();
10995     },
10996     
10997     indicatorEl : function()
10998     {
10999         if (Roo.bootstrap.version == 4) {
11000             return false; // not enabled in v4 yet.
11001         }
11002         
11003         var indicator = this.el.select('i.roo-required-indicator',true).first();
11004         
11005         if(!indicator){
11006             return false;
11007         }
11008         
11009         return indicator;
11010         
11011     },
11012     
11013     setDisabled : function(v)
11014     {
11015         var i  = this.inputEl().dom;
11016         if (!v) {
11017             i.removeAttribute('disabled');
11018             return;
11019             
11020         }
11021         i.setAttribute('disabled','true');
11022     },
11023     initEvents : function()
11024     {
11025           
11026         this.inputEl().on("keydown" , this.fireKey,  this);
11027         this.inputEl().on("focus", this.onFocus,  this);
11028         this.inputEl().on("blur", this.onBlur,  this);
11029         
11030         this.inputEl().relayEvent('keyup', this);
11031         this.inputEl().relayEvent('paste', this);
11032         
11033         this.indicator = this.indicatorEl();
11034         
11035         if(this.indicator){
11036             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11037         }
11038  
11039         // reference to original value for reset
11040         this.originalValue = this.getValue();
11041         //Roo.form.TextField.superclass.initEvents.call(this);
11042         if(this.validationEvent == 'keyup'){
11043             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11044             this.inputEl().on('keyup', this.filterValidation, this);
11045         }
11046         else if(this.validationEvent !== false){
11047             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11048         }
11049         
11050         if(this.selectOnFocus){
11051             this.on("focus", this.preFocus, this);
11052             
11053         }
11054         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11055             this.inputEl().on("keypress", this.filterKeys, this);
11056         } else {
11057             this.inputEl().relayEvent('keypress', this);
11058         }
11059        /* if(this.grow){
11060             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11061             this.el.on("click", this.autoSize,  this);
11062         }
11063         */
11064         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11065             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11066         }
11067         
11068         if (typeof(this.before) == 'object') {
11069             this.before.render(this.el.select('.roo-input-before',true).first());
11070         }
11071         if (typeof(this.after) == 'object') {
11072             this.after.render(this.el.select('.roo-input-after',true).first());
11073         }
11074         
11075         this.inputEl().on('change', this.onChange, this);
11076         
11077     },
11078     filterValidation : function(e){
11079         if(!e.isNavKeyPress()){
11080             this.validationTask.delay(this.validationDelay);
11081         }
11082     },
11083      /**
11084      * Validates the field value
11085      * @return {Boolean} True if the value is valid, else false
11086      */
11087     validate : function(){
11088         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11089         if(this.disabled || this.validateValue(this.getRawValue())){
11090             this.markValid();
11091             return true;
11092         }
11093         
11094         this.markInvalid();
11095         return false;
11096     },
11097     
11098     
11099     /**
11100      * Validates a value according to the field's validation rules and marks the field as invalid
11101      * if the validation fails
11102      * @param {Mixed} value The value to validate
11103      * @return {Boolean} True if the value is valid, else false
11104      */
11105     validateValue : function(value)
11106     {
11107         if(this.getVisibilityEl().hasClass('hidden')){
11108             return true;
11109         }
11110         
11111         if(value.length < 1)  { // if it's blank
11112             if(this.allowBlank){
11113                 return true;
11114             }
11115             return false;
11116         }
11117         
11118         if(value.length < this.minLength){
11119             return false;
11120         }
11121         if(value.length > this.maxLength){
11122             return false;
11123         }
11124         if(this.vtype){
11125             var vt = Roo.form.VTypes;
11126             if(!vt[this.vtype](value, this)){
11127                 return false;
11128             }
11129         }
11130         if(typeof this.validator == "function"){
11131             var msg = this.validator(value);
11132             if(msg !== true){
11133                 return false;
11134             }
11135             if (typeof(msg) == 'string') {
11136                 this.invalidText = msg;
11137             }
11138         }
11139         
11140         if(this.regex && !this.regex.test(value)){
11141             return false;
11142         }
11143         
11144         return true;
11145     },
11146     
11147      // private
11148     fireKey : function(e){
11149         //Roo.log('field ' + e.getKey());
11150         if(e.isNavKeyPress()){
11151             this.fireEvent("specialkey", this, e);
11152         }
11153     },
11154     focus : function (selectText){
11155         if(this.rendered){
11156             this.inputEl().focus();
11157             if(selectText === true){
11158                 this.inputEl().dom.select();
11159             }
11160         }
11161         return this;
11162     } ,
11163     
11164     onFocus : function(){
11165         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11166            // this.el.addClass(this.focusClass);
11167         }
11168         if(!this.hasFocus){
11169             this.hasFocus = true;
11170             this.startValue = this.getValue();
11171             this.fireEvent("focus", this);
11172         }
11173     },
11174     
11175     beforeBlur : Roo.emptyFn,
11176
11177     
11178     // private
11179     onBlur : function(){
11180         this.beforeBlur();
11181         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11182             //this.el.removeClass(this.focusClass);
11183         }
11184         this.hasFocus = false;
11185         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11186             this.validate();
11187         }
11188         var v = this.getValue();
11189         if(String(v) !== String(this.startValue)){
11190             this.fireEvent('change', this, v, this.startValue);
11191         }
11192         this.fireEvent("blur", this);
11193     },
11194     
11195     onChange : function(e)
11196     {
11197         var v = this.getValue();
11198         if(String(v) !== String(this.startValue)){
11199             this.fireEvent('change', this, v, this.startValue);
11200         }
11201         
11202     },
11203     
11204     /**
11205      * Resets the current field value to the originally loaded value and clears any validation messages
11206      */
11207     reset : function(){
11208         this.setValue(this.originalValue);
11209         this.validate();
11210     },
11211      /**
11212      * Returns the name of the field
11213      * @return {Mixed} name The name field
11214      */
11215     getName: function(){
11216         return this.name;
11217     },
11218      /**
11219      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11220      * @return {Mixed} value The field value
11221      */
11222     getValue : function(){
11223         
11224         var v = this.inputEl().getValue();
11225         
11226         return v;
11227     },
11228     /**
11229      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11230      * @return {Mixed} value The field value
11231      */
11232     getRawValue : function(){
11233         var v = this.inputEl().getValue();
11234         
11235         return v;
11236     },
11237     
11238     /**
11239      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11240      * @param {Mixed} value The value to set
11241      */
11242     setRawValue : function(v){
11243         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11244     },
11245     
11246     selectText : function(start, end){
11247         var v = this.getRawValue();
11248         if(v.length > 0){
11249             start = start === undefined ? 0 : start;
11250             end = end === undefined ? v.length : end;
11251             var d = this.inputEl().dom;
11252             if(d.setSelectionRange){
11253                 d.setSelectionRange(start, end);
11254             }else if(d.createTextRange){
11255                 var range = d.createTextRange();
11256                 range.moveStart("character", start);
11257                 range.moveEnd("character", v.length-end);
11258                 range.select();
11259             }
11260         }
11261     },
11262     
11263     /**
11264      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11265      * @param {Mixed} value The value to set
11266      */
11267     setValue : function(v){
11268         this.value = v;
11269         if(this.rendered){
11270             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11271             this.validate();
11272         }
11273     },
11274     
11275     /*
11276     processValue : function(value){
11277         if(this.stripCharsRe){
11278             var newValue = value.replace(this.stripCharsRe, '');
11279             if(newValue !== value){
11280                 this.setRawValue(newValue);
11281                 return newValue;
11282             }
11283         }
11284         return value;
11285     },
11286   */
11287     preFocus : function(){
11288         
11289         if(this.selectOnFocus){
11290             this.inputEl().dom.select();
11291         }
11292     },
11293     filterKeys : function(e){
11294         var k = e.getKey();
11295         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11296             return;
11297         }
11298         var c = e.getCharCode(), cc = String.fromCharCode(c);
11299         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11300             return;
11301         }
11302         if(!this.maskRe.test(cc)){
11303             e.stopEvent();
11304         }
11305     },
11306      /**
11307      * Clear any invalid styles/messages for this field
11308      */
11309     clearInvalid : function(){
11310         
11311         if(!this.el || this.preventMark){ // not rendered
11312             return;
11313         }
11314         
11315         
11316         this.el.removeClass([this.invalidClass, 'is-invalid']);
11317         
11318         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11319             
11320             var feedback = this.el.select('.form-control-feedback', true).first();
11321             
11322             if(feedback){
11323                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11324             }
11325             
11326         }
11327         
11328         if(this.indicator){
11329             this.indicator.removeClass('visible');
11330             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11331         }
11332         
11333         this.fireEvent('valid', this);
11334     },
11335     
11336      /**
11337      * Mark this field as valid
11338      */
11339     markValid : function()
11340     {
11341         if(!this.el  || this.preventMark){ // not rendered...
11342             return;
11343         }
11344         
11345         this.el.removeClass([this.invalidClass, this.validClass]);
11346         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11347
11348         var feedback = this.el.select('.form-control-feedback', true).first();
11349             
11350         if(feedback){
11351             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11352         }
11353         
11354         if(this.indicator){
11355             this.indicator.removeClass('visible');
11356             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11357         }
11358         
11359         if(this.disabled){
11360             return;
11361         }
11362         
11363            
11364         if(this.allowBlank && !this.getRawValue().length){
11365             return;
11366         }
11367         if (Roo.bootstrap.version == 3) {
11368             this.el.addClass(this.validClass);
11369         } else {
11370             this.inputEl().addClass('is-valid');
11371         }
11372
11373         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11374             
11375             var feedback = this.el.select('.form-control-feedback', true).first();
11376             
11377             if(feedback){
11378                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11379                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11380             }
11381             
11382         }
11383         
11384         this.fireEvent('valid', this);
11385     },
11386     
11387      /**
11388      * Mark this field as invalid
11389      * @param {String} msg The validation message
11390      */
11391     markInvalid : function(msg)
11392     {
11393         if(!this.el  || this.preventMark){ // not rendered
11394             return;
11395         }
11396         
11397         this.el.removeClass([this.invalidClass, this.validClass]);
11398         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11399         
11400         var feedback = this.el.select('.form-control-feedback', true).first();
11401             
11402         if(feedback){
11403             this.el.select('.form-control-feedback', true).first().removeClass(
11404                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11405         }
11406
11407         if(this.disabled){
11408             return;
11409         }
11410         
11411         if(this.allowBlank && !this.getRawValue().length){
11412             return;
11413         }
11414         
11415         if(this.indicator){
11416             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11417             this.indicator.addClass('visible');
11418         }
11419         if (Roo.bootstrap.version == 3) {
11420             this.el.addClass(this.invalidClass);
11421         } else {
11422             this.inputEl().addClass('is-invalid');
11423         }
11424         
11425         
11426         
11427         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11428             
11429             var feedback = this.el.select('.form-control-feedback', true).first();
11430             
11431             if(feedback){
11432                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11433                 
11434                 if(this.getValue().length || this.forceFeedback){
11435                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11436                 }
11437                 
11438             }
11439             
11440         }
11441         
11442         this.fireEvent('invalid', this, msg);
11443     },
11444     // private
11445     SafariOnKeyDown : function(event)
11446     {
11447         // this is a workaround for a password hang bug on chrome/ webkit.
11448         if (this.inputEl().dom.type != 'password') {
11449             return;
11450         }
11451         
11452         var isSelectAll = false;
11453         
11454         if(this.inputEl().dom.selectionEnd > 0){
11455             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11456         }
11457         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11458             event.preventDefault();
11459             this.setValue('');
11460             return;
11461         }
11462         
11463         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11464             
11465             event.preventDefault();
11466             // this is very hacky as keydown always get's upper case.
11467             //
11468             var cc = String.fromCharCode(event.getCharCode());
11469             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11470             
11471         }
11472     },
11473     adjustWidth : function(tag, w){
11474         tag = tag.toLowerCase();
11475         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11476             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11477                 if(tag == 'input'){
11478                     return w + 2;
11479                 }
11480                 if(tag == 'textarea'){
11481                     return w-2;
11482                 }
11483             }else if(Roo.isOpera){
11484                 if(tag == 'input'){
11485                     return w + 2;
11486                 }
11487                 if(tag == 'textarea'){
11488                     return w-2;
11489                 }
11490             }
11491         }
11492         return w;
11493     },
11494     
11495     setFieldLabel : function(v)
11496     {
11497         if(!this.rendered){
11498             return;
11499         }
11500         
11501         if(this.indicatorEl()){
11502             var ar = this.el.select('label > span',true);
11503             
11504             if (ar.elements.length) {
11505                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11506                 this.fieldLabel = v;
11507                 return;
11508             }
11509             
11510             var br = this.el.select('label',true);
11511             
11512             if(br.elements.length) {
11513                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11514                 this.fieldLabel = v;
11515                 return;
11516             }
11517             
11518             Roo.log('Cannot Found any of label > span || label in input');
11519             return;
11520         }
11521         
11522         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11523         this.fieldLabel = v;
11524         
11525         
11526     }
11527 });
11528
11529  
11530 /*
11531  * - LGPL
11532  *
11533  * Input
11534  * 
11535  */
11536
11537 /**
11538  * @class Roo.bootstrap.TextArea
11539  * @extends Roo.bootstrap.Input
11540  * Bootstrap TextArea class
11541  * @cfg {Number} cols Specifies the visible width of a text area
11542  * @cfg {Number} rows Specifies the visible number of lines in a text area
11543  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11544  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11545  * @cfg {string} html text
11546  * 
11547  * @constructor
11548  * Create a new TextArea
11549  * @param {Object} config The config object
11550  */
11551
11552 Roo.bootstrap.TextArea = function(config){
11553     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11554    
11555 };
11556
11557 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11558      
11559     cols : false,
11560     rows : 5,
11561     readOnly : false,
11562     warp : 'soft',
11563     resize : false,
11564     value: false,
11565     html: false,
11566     
11567     getAutoCreate : function(){
11568         
11569         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11570         
11571         var id = Roo.id();
11572         
11573         var cfg = {};
11574         
11575         if(this.inputType != 'hidden'){
11576             cfg.cls = 'form-group' //input-group
11577         }
11578         
11579         var input =  {
11580             tag: 'textarea',
11581             id : id,
11582             warp : this.warp,
11583             rows : this.rows,
11584             value : this.value || '',
11585             html: this.html || '',
11586             cls : 'form-control',
11587             placeholder : this.placeholder || '' 
11588             
11589         };
11590         
11591         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11592             input.maxLength = this.maxLength;
11593         }
11594         
11595         if(this.resize){
11596             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11597         }
11598         
11599         if(this.cols){
11600             input.cols = this.cols;
11601         }
11602         
11603         if (this.readOnly) {
11604             input.readonly = true;
11605         }
11606         
11607         if (this.name) {
11608             input.name = this.name;
11609         }
11610         
11611         if (this.size) {
11612             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11613         }
11614         
11615         var settings=this;
11616         ['xs','sm','md','lg'].map(function(size){
11617             if (settings[size]) {
11618                 cfg.cls += ' col-' + size + '-' + settings[size];
11619             }
11620         });
11621         
11622         var inputblock = input;
11623         
11624         if(this.hasFeedback && !this.allowBlank){
11625             
11626             var feedback = {
11627                 tag: 'span',
11628                 cls: 'glyphicon form-control-feedback'
11629             };
11630
11631             inputblock = {
11632                 cls : 'has-feedback',
11633                 cn :  [
11634                     input,
11635                     feedback
11636                 ] 
11637             };  
11638         }
11639         
11640         
11641         if (this.before || this.after) {
11642             
11643             inputblock = {
11644                 cls : 'input-group',
11645                 cn :  [] 
11646             };
11647             if (this.before) {
11648                 inputblock.cn.push({
11649                     tag :'span',
11650                     cls : 'input-group-addon',
11651                     html : this.before
11652                 });
11653             }
11654             
11655             inputblock.cn.push(input);
11656             
11657             if(this.hasFeedback && !this.allowBlank){
11658                 inputblock.cls += ' has-feedback';
11659                 inputblock.cn.push(feedback);
11660             }
11661             
11662             if (this.after) {
11663                 inputblock.cn.push({
11664                     tag :'span',
11665                     cls : 'input-group-addon',
11666                     html : this.after
11667                 });
11668             }
11669             
11670         }
11671         
11672         if (align ==='left' && this.fieldLabel.length) {
11673             cfg.cn = [
11674                 {
11675                     tag: 'label',
11676                     'for' :  id,
11677                     cls : 'control-label',
11678                     html : this.fieldLabel
11679                 },
11680                 {
11681                     cls : "",
11682                     cn: [
11683                         inputblock
11684                     ]
11685                 }
11686
11687             ];
11688             
11689             if(this.labelWidth > 12){
11690                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11691             }
11692
11693             if(this.labelWidth < 13 && this.labelmd == 0){
11694                 this.labelmd = this.labelWidth;
11695             }
11696
11697             if(this.labellg > 0){
11698                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11699                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11700             }
11701
11702             if(this.labelmd > 0){
11703                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11704                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11705             }
11706
11707             if(this.labelsm > 0){
11708                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11709                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11710             }
11711
11712             if(this.labelxs > 0){
11713                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11714                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11715             }
11716             
11717         } else if ( this.fieldLabel.length) {
11718             cfg.cn = [
11719
11720                {
11721                    tag: 'label',
11722                    //cls : 'input-group-addon',
11723                    html : this.fieldLabel
11724
11725                },
11726
11727                inputblock
11728
11729            ];
11730
11731         } else {
11732
11733             cfg.cn = [
11734
11735                 inputblock
11736
11737             ];
11738                 
11739         }
11740         
11741         if (this.disabled) {
11742             input.disabled=true;
11743         }
11744         
11745         return cfg;
11746         
11747     },
11748     /**
11749      * return the real textarea element.
11750      */
11751     inputEl: function ()
11752     {
11753         return this.el.select('textarea.form-control',true).first();
11754     },
11755     
11756     /**
11757      * Clear any invalid styles/messages for this field
11758      */
11759     clearInvalid : function()
11760     {
11761         
11762         if(!this.el || this.preventMark){ // not rendered
11763             return;
11764         }
11765         
11766         var label = this.el.select('label', true).first();
11767         var icon = this.el.select('i.fa-star', true).first();
11768         
11769         if(label && icon){
11770             icon.remove();
11771         }
11772         this.el.removeClass( this.validClass);
11773         this.inputEl().removeClass('is-invalid');
11774          
11775         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11776             
11777             var feedback = this.el.select('.form-control-feedback', true).first();
11778             
11779             if(feedback){
11780                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11781             }
11782             
11783         }
11784         
11785         this.fireEvent('valid', this);
11786     },
11787     
11788      /**
11789      * Mark this field as valid
11790      */
11791     markValid : function()
11792     {
11793         if(!this.el  || this.preventMark){ // not rendered
11794             return;
11795         }
11796         
11797         this.el.removeClass([this.invalidClass, this.validClass]);
11798         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11799         
11800         var feedback = this.el.select('.form-control-feedback', true).first();
11801             
11802         if(feedback){
11803             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11804         }
11805
11806         if(this.disabled || this.allowBlank){
11807             return;
11808         }
11809         
11810         var label = this.el.select('label', true).first();
11811         var icon = this.el.select('i.fa-star', true).first();
11812         
11813         if(label && icon){
11814             icon.remove();
11815         }
11816         if (Roo.bootstrap.version == 3) {
11817             this.el.addClass(this.validClass);
11818         } else {
11819             this.inputEl().addClass('is-valid');
11820         }
11821         
11822         
11823         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11824             
11825             var feedback = this.el.select('.form-control-feedback', true).first();
11826             
11827             if(feedback){
11828                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11829                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11830             }
11831             
11832         }
11833         
11834         this.fireEvent('valid', this);
11835     },
11836     
11837      /**
11838      * Mark this field as invalid
11839      * @param {String} msg The validation message
11840      */
11841     markInvalid : function(msg)
11842     {
11843         if(!this.el  || this.preventMark){ // not rendered
11844             return;
11845         }
11846         
11847         this.el.removeClass([this.invalidClass, this.validClass]);
11848         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11849         
11850         var feedback = this.el.select('.form-control-feedback', true).first();
11851             
11852         if(feedback){
11853             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11854         }
11855
11856         if(this.disabled || this.allowBlank){
11857             return;
11858         }
11859         
11860         var label = this.el.select('label', true).first();
11861         var icon = this.el.select('i.fa-star', true).first();
11862         
11863         if(!this.getValue().length && label && !icon){
11864             this.el.createChild({
11865                 tag : 'i',
11866                 cls : 'text-danger fa fa-lg fa-star',
11867                 tooltip : 'This field is required',
11868                 style : 'margin-right:5px;'
11869             }, label, true);
11870         }
11871         
11872         if (Roo.bootstrap.version == 3) {
11873             this.el.addClass(this.invalidClass);
11874         } else {
11875             this.inputEl().addClass('is-invalid');
11876         }
11877         
11878         // fixme ... this may be depricated need to test..
11879         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11880             
11881             var feedback = this.el.select('.form-control-feedback', true).first();
11882             
11883             if(feedback){
11884                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11885                 
11886                 if(this.getValue().length || this.forceFeedback){
11887                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11888                 }
11889                 
11890             }
11891             
11892         }
11893         
11894         this.fireEvent('invalid', this, msg);
11895     }
11896 });
11897
11898  
11899 /*
11900  * - LGPL
11901  *
11902  * trigger field - base class for combo..
11903  * 
11904  */
11905  
11906 /**
11907  * @class Roo.bootstrap.TriggerField
11908  * @extends Roo.bootstrap.Input
11909  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11910  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11911  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11912  * for which you can provide a custom implementation.  For example:
11913  * <pre><code>
11914 var trigger = new Roo.bootstrap.TriggerField();
11915 trigger.onTriggerClick = myTriggerFn;
11916 trigger.applyTo('my-field');
11917 </code></pre>
11918  *
11919  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11920  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11921  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11922  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11923  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11924
11925  * @constructor
11926  * Create a new TriggerField.
11927  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11928  * to the base TextField)
11929  */
11930 Roo.bootstrap.TriggerField = function(config){
11931     this.mimicing = false;
11932     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11933 };
11934
11935 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11936     /**
11937      * @cfg {String} triggerClass A CSS class to apply to the trigger
11938      */
11939      /**
11940      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11941      */
11942     hideTrigger:false,
11943
11944     /**
11945      * @cfg {Boolean} removable (true|false) special filter default false
11946      */
11947     removable : false,
11948     
11949     /** @cfg {Boolean} grow @hide */
11950     /** @cfg {Number} growMin @hide */
11951     /** @cfg {Number} growMax @hide */
11952
11953     /**
11954      * @hide 
11955      * @method
11956      */
11957     autoSize: Roo.emptyFn,
11958     // private
11959     monitorTab : true,
11960     // private
11961     deferHeight : true,
11962
11963     
11964     actionMode : 'wrap',
11965     
11966     caret : false,
11967     
11968     
11969     getAutoCreate : function(){
11970        
11971         var align = this.labelAlign || this.parentLabelAlign();
11972         
11973         var id = Roo.id();
11974         
11975         var cfg = {
11976             cls: 'form-group' //input-group
11977         };
11978         
11979         
11980         var input =  {
11981             tag: 'input',
11982             id : id,
11983             type : this.inputType,
11984             cls : 'form-control',
11985             autocomplete: 'new-password',
11986             placeholder : this.placeholder || '' 
11987             
11988         };
11989         if (this.name) {
11990             input.name = this.name;
11991         }
11992         if (this.size) {
11993             input.cls += ' input-' + this.size;
11994         }
11995         
11996         if (this.disabled) {
11997             input.disabled=true;
11998         }
11999         
12000         var inputblock = input;
12001         
12002         if(this.hasFeedback && !this.allowBlank){
12003             
12004             var feedback = {
12005                 tag: 'span',
12006                 cls: 'glyphicon form-control-feedback'
12007             };
12008             
12009             if(this.removable && !this.editable  ){
12010                 inputblock = {
12011                     cls : 'has-feedback',
12012                     cn :  [
12013                         inputblock,
12014                         {
12015                             tag: 'button',
12016                             html : 'x',
12017                             cls : 'roo-combo-removable-btn close'
12018                         },
12019                         feedback
12020                     ] 
12021                 };
12022             } else {
12023                 inputblock = {
12024                     cls : 'has-feedback',
12025                     cn :  [
12026                         inputblock,
12027                         feedback
12028                     ] 
12029                 };
12030             }
12031
12032         } else {
12033             if(this.removable && !this.editable ){
12034                 inputblock = {
12035                     cls : 'roo-removable',
12036                     cn :  [
12037                         inputblock,
12038                         {
12039                             tag: 'button',
12040                             html : 'x',
12041                             cls : 'roo-combo-removable-btn close'
12042                         }
12043                     ] 
12044                 };
12045             }
12046         }
12047         
12048         if (this.before || this.after) {
12049             
12050             inputblock = {
12051                 cls : 'input-group',
12052                 cn :  [] 
12053             };
12054             if (this.before) {
12055                 inputblock.cn.push({
12056                     tag :'span',
12057                     cls : 'input-group-addon input-group-prepend input-group-text',
12058                     html : this.before
12059                 });
12060             }
12061             
12062             inputblock.cn.push(input);
12063             
12064             if(this.hasFeedback && !this.allowBlank){
12065                 inputblock.cls += ' has-feedback';
12066                 inputblock.cn.push(feedback);
12067             }
12068             
12069             if (this.after) {
12070                 inputblock.cn.push({
12071                     tag :'span',
12072                     cls : 'input-group-addon input-group-append input-group-text',
12073                     html : this.after
12074                 });
12075             }
12076             
12077         };
12078         
12079       
12080         
12081         var ibwrap = inputblock;
12082         
12083         if(this.multiple){
12084             ibwrap = {
12085                 tag: 'ul',
12086                 cls: 'roo-select2-choices',
12087                 cn:[
12088                     {
12089                         tag: 'li',
12090                         cls: 'roo-select2-search-field',
12091                         cn: [
12092
12093                             inputblock
12094                         ]
12095                     }
12096                 ]
12097             };
12098                 
12099         }
12100         
12101         var combobox = {
12102             cls: 'roo-select2-container input-group',
12103             cn: [
12104                  {
12105                     tag: 'input',
12106                     type : 'hidden',
12107                     cls: 'form-hidden-field'
12108                 },
12109                 ibwrap
12110             ]
12111         };
12112         
12113         if(!this.multiple && this.showToggleBtn){
12114             
12115             var caret = {
12116                         tag: 'span',
12117                         cls: 'caret'
12118              };
12119             if (this.caret != false) {
12120                 caret = {
12121                      tag: 'i',
12122                      cls: 'fa fa-' + this.caret
12123                 };
12124                 
12125             }
12126             
12127             combobox.cn.push({
12128                 tag :'span',
12129                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12130                 cn : [
12131                     Roo.bootstrap.version == 3 ? caret : '',
12132                     {
12133                         tag: 'span',
12134                         cls: 'combobox-clear',
12135                         cn  : [
12136                             {
12137                                 tag : 'i',
12138                                 cls: 'icon-remove'
12139                             }
12140                         ]
12141                     }
12142                 ]
12143
12144             })
12145         }
12146         
12147         if(this.multiple){
12148             combobox.cls += ' roo-select2-container-multi';
12149         }
12150          var indicator = {
12151             tag : 'i',
12152             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12153             tooltip : 'This field is required'
12154         };
12155         if (Roo.bootstrap.version == 4) {
12156             indicator = {
12157                 tag : 'i',
12158                 style : 'display:none'
12159             };
12160         }
12161         
12162         
12163         if (align ==='left' && this.fieldLabel.length) {
12164             
12165             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12166
12167             cfg.cn = [
12168                 indicator,
12169                 {
12170                     tag: 'label',
12171                     'for' :  id,
12172                     cls : 'control-label',
12173                     html : this.fieldLabel
12174
12175                 },
12176                 {
12177                     cls : "", 
12178                     cn: [
12179                         combobox
12180                     ]
12181                 }
12182
12183             ];
12184             
12185             var labelCfg = cfg.cn[1];
12186             var contentCfg = cfg.cn[2];
12187             
12188             if(this.indicatorpos == 'right'){
12189                 cfg.cn = [
12190                     {
12191                         tag: 'label',
12192                         'for' :  id,
12193                         cls : 'control-label',
12194                         cn : [
12195                             {
12196                                 tag : 'span',
12197                                 html : this.fieldLabel
12198                             },
12199                             indicator
12200                         ]
12201                     },
12202                     {
12203                         cls : "", 
12204                         cn: [
12205                             combobox
12206                         ]
12207                     }
12208
12209                 ];
12210                 
12211                 labelCfg = cfg.cn[0];
12212                 contentCfg = cfg.cn[1];
12213             }
12214             
12215             if(this.labelWidth > 12){
12216                 labelCfg.style = "width: " + this.labelWidth + 'px';
12217             }
12218             
12219             if(this.labelWidth < 13 && this.labelmd == 0){
12220                 this.labelmd = this.labelWidth;
12221             }
12222             
12223             if(this.labellg > 0){
12224                 labelCfg.cls += ' col-lg-' + this.labellg;
12225                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12226             }
12227             
12228             if(this.labelmd > 0){
12229                 labelCfg.cls += ' col-md-' + this.labelmd;
12230                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12231             }
12232             
12233             if(this.labelsm > 0){
12234                 labelCfg.cls += ' col-sm-' + this.labelsm;
12235                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12236             }
12237             
12238             if(this.labelxs > 0){
12239                 labelCfg.cls += ' col-xs-' + this.labelxs;
12240                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12241             }
12242             
12243         } else if ( this.fieldLabel.length) {
12244 //                Roo.log(" label");
12245             cfg.cn = [
12246                 indicator,
12247                {
12248                    tag: 'label',
12249                    //cls : 'input-group-addon',
12250                    html : this.fieldLabel
12251
12252                },
12253
12254                combobox
12255
12256             ];
12257             
12258             if(this.indicatorpos == 'right'){
12259                 
12260                 cfg.cn = [
12261                     {
12262                        tag: 'label',
12263                        cn : [
12264                            {
12265                                tag : 'span',
12266                                html : this.fieldLabel
12267                            },
12268                            indicator
12269                        ]
12270
12271                     },
12272                     combobox
12273
12274                 ];
12275
12276             }
12277
12278         } else {
12279             
12280 //                Roo.log(" no label && no align");
12281                 cfg = combobox
12282                      
12283                 
12284         }
12285         
12286         var settings=this;
12287         ['xs','sm','md','lg'].map(function(size){
12288             if (settings[size]) {
12289                 cfg.cls += ' col-' + size + '-' + settings[size];
12290             }
12291         });
12292         
12293         return cfg;
12294         
12295     },
12296     
12297     
12298     
12299     // private
12300     onResize : function(w, h){
12301 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12302 //        if(typeof w == 'number'){
12303 //            var x = w - this.trigger.getWidth();
12304 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12305 //            this.trigger.setStyle('left', x+'px');
12306 //        }
12307     },
12308
12309     // private
12310     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12311
12312     // private
12313     getResizeEl : function(){
12314         return this.inputEl();
12315     },
12316
12317     // private
12318     getPositionEl : function(){
12319         return this.inputEl();
12320     },
12321
12322     // private
12323     alignErrorIcon : function(){
12324         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12325     },
12326
12327     // private
12328     initEvents : function(){
12329         
12330         this.createList();
12331         
12332         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12333         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12334         if(!this.multiple && this.showToggleBtn){
12335             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12336             if(this.hideTrigger){
12337                 this.trigger.setDisplayed(false);
12338             }
12339             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12340         }
12341         
12342         if(this.multiple){
12343             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12344         }
12345         
12346         if(this.removable && !this.editable && !this.tickable){
12347             var close = this.closeTriggerEl();
12348             
12349             if(close){
12350                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12351                 close.on('click', this.removeBtnClick, this, close);
12352             }
12353         }
12354         
12355         //this.trigger.addClassOnOver('x-form-trigger-over');
12356         //this.trigger.addClassOnClick('x-form-trigger-click');
12357         
12358         //if(!this.width){
12359         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12360         //}
12361     },
12362     
12363     closeTriggerEl : function()
12364     {
12365         var close = this.el.select('.roo-combo-removable-btn', true).first();
12366         return close ? close : false;
12367     },
12368     
12369     removeBtnClick : function(e, h, el)
12370     {
12371         e.preventDefault();
12372         
12373         if(this.fireEvent("remove", this) !== false){
12374             this.reset();
12375             this.fireEvent("afterremove", this)
12376         }
12377     },
12378     
12379     createList : function()
12380     {
12381         this.list = Roo.get(document.body).createChild({
12382             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12383             cls: 'typeahead typeahead-long dropdown-menu shadow',
12384             style: 'display:none'
12385         });
12386         
12387         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12388         
12389     },
12390
12391     // private
12392     initTrigger : function(){
12393        
12394     },
12395
12396     // private
12397     onDestroy : function(){
12398         if(this.trigger){
12399             this.trigger.removeAllListeners();
12400           //  this.trigger.remove();
12401         }
12402         //if(this.wrap){
12403         //    this.wrap.remove();
12404         //}
12405         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12406     },
12407
12408     // private
12409     onFocus : function(){
12410         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12411         /*
12412         if(!this.mimicing){
12413             this.wrap.addClass('x-trigger-wrap-focus');
12414             this.mimicing = true;
12415             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12416             if(this.monitorTab){
12417                 this.el.on("keydown", this.checkTab, this);
12418             }
12419         }
12420         */
12421     },
12422
12423     // private
12424     checkTab : function(e){
12425         if(e.getKey() == e.TAB){
12426             this.triggerBlur();
12427         }
12428     },
12429
12430     // private
12431     onBlur : function(){
12432         // do nothing
12433     },
12434
12435     // private
12436     mimicBlur : function(e, t){
12437         /*
12438         if(!this.wrap.contains(t) && this.validateBlur()){
12439             this.triggerBlur();
12440         }
12441         */
12442     },
12443
12444     // private
12445     triggerBlur : function(){
12446         this.mimicing = false;
12447         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12448         if(this.monitorTab){
12449             this.el.un("keydown", this.checkTab, this);
12450         }
12451         //this.wrap.removeClass('x-trigger-wrap-focus');
12452         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12453     },
12454
12455     // private
12456     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12457     validateBlur : function(e, t){
12458         return true;
12459     },
12460
12461     // private
12462     onDisable : function(){
12463         this.inputEl().dom.disabled = true;
12464         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12465         //if(this.wrap){
12466         //    this.wrap.addClass('x-item-disabled');
12467         //}
12468     },
12469
12470     // private
12471     onEnable : function(){
12472         this.inputEl().dom.disabled = false;
12473         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12474         //if(this.wrap){
12475         //    this.el.removeClass('x-item-disabled');
12476         //}
12477     },
12478
12479     // private
12480     onShow : function(){
12481         var ae = this.getActionEl();
12482         
12483         if(ae){
12484             ae.dom.style.display = '';
12485             ae.dom.style.visibility = 'visible';
12486         }
12487     },
12488
12489     // private
12490     
12491     onHide : function(){
12492         var ae = this.getActionEl();
12493         ae.dom.style.display = 'none';
12494     },
12495
12496     /**
12497      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12498      * by an implementing function.
12499      * @method
12500      * @param {EventObject} e
12501      */
12502     onTriggerClick : Roo.emptyFn
12503 });
12504  
12505 /*
12506 * Licence: LGPL
12507 */
12508
12509 /**
12510  * @class Roo.bootstrap.CardUploader
12511  * @extends Roo.bootstrap.Button
12512  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12513  * @cfg {Number} errorTimeout default 3000
12514  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12515  * @cfg {Array}  html The button text.
12516
12517  *
12518  * @constructor
12519  * Create a new CardUploader
12520  * @param {Object} config The config object
12521  */
12522
12523 Roo.bootstrap.CardUploader = function(config){
12524     
12525  
12526     
12527     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12528     
12529     
12530     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12531         return r.data.id
12532      });
12533     
12534      this.addEvents({
12535          // raw events
12536         /**
12537          * @event preview
12538          * When a image is clicked on - and needs to display a slideshow or similar..
12539          * @param {Roo.bootstrap.Card} this
12540          * @param {Object} The image information data 
12541          *
12542          */
12543         'preview' : true,
12544          /**
12545          * @event download
12546          * When a the download link is clicked
12547          * @param {Roo.bootstrap.Card} this
12548          * @param {Object} The image information data  contains 
12549          */
12550         'download' : true
12551         
12552     });
12553 };
12554  
12555 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12556     
12557      
12558     errorTimeout : 3000,
12559      
12560     images : false,
12561    
12562     fileCollection : false,
12563     allowBlank : true,
12564     
12565     getAutoCreate : function()
12566     {
12567         
12568         var cfg =  {
12569             cls :'form-group' ,
12570             cn : [
12571                
12572                 {
12573                     tag: 'label',
12574                    //cls : 'input-group-addon',
12575                     html : this.fieldLabel
12576
12577                 },
12578
12579                 {
12580                     tag: 'input',
12581                     type : 'hidden',
12582                     name : this.name,
12583                     value : this.value,
12584                     cls : 'd-none  form-control'
12585                 },
12586                 
12587                 {
12588                     tag: 'input',
12589                     multiple : 'multiple',
12590                     type : 'file',
12591                     cls : 'd-none  roo-card-upload-selector'
12592                 },
12593                 
12594                 {
12595                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12596                 },
12597                 {
12598                     cls : 'card-columns roo-card-uploader-container'
12599                 }
12600
12601             ]
12602         };
12603            
12604          
12605         return cfg;
12606     },
12607     
12608     getChildContainer : function() /// what children are added to.
12609     {
12610         return this.containerEl;
12611     },
12612    
12613     getButtonContainer : function() /// what children are added to.
12614     {
12615         return this.el.select(".roo-card-uploader-button-container").first();
12616     },
12617    
12618     initEvents : function()
12619     {
12620         
12621         Roo.bootstrap.Input.prototype.initEvents.call(this);
12622         
12623         var t = this;
12624         this.addxtype({
12625             xns: Roo.bootstrap,
12626
12627             xtype : 'Button',
12628             container_method : 'getButtonContainer' ,            
12629             html :  this.html, // fix changable?
12630             cls : 'w-100 ',
12631             listeners : {
12632                 'click' : function(btn, e) {
12633                     t.onClick(e);
12634                 }
12635             }
12636         });
12637         
12638         
12639         
12640         
12641         this.urlAPI = (window.createObjectURL && window) || 
12642                                 (window.URL && URL.revokeObjectURL && URL) || 
12643                                 (window.webkitURL && webkitURL);
12644                         
12645          
12646          
12647          
12648         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12649         
12650         this.selectorEl.on('change', this.onFileSelected, this);
12651         if (this.images) {
12652             var t = this;
12653             this.images.forEach(function(img) {
12654                 t.addCard(img)
12655             });
12656             this.images = false;
12657         }
12658         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12659          
12660        
12661     },
12662     
12663    
12664     onClick : function(e)
12665     {
12666         e.preventDefault();
12667          
12668         this.selectorEl.dom.click();
12669          
12670     },
12671     
12672     onFileSelected : function(e)
12673     {
12674         e.preventDefault();
12675         
12676         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12677             return;
12678         }
12679         
12680         Roo.each(this.selectorEl.dom.files, function(file){    
12681             this.addFile(file);
12682         }, this);
12683          
12684     },
12685     
12686       
12687     
12688       
12689     
12690     addFile : function(file)
12691     {
12692            
12693         if(typeof(file) === 'string'){
12694             throw "Add file by name?"; // should not happen
12695             return;
12696         }
12697         
12698         if(!file || !this.urlAPI){
12699             return;
12700         }
12701         
12702         // file;
12703         // file.type;
12704         
12705         var _this = this;
12706         
12707         
12708         var url = _this.urlAPI.createObjectURL( file);
12709            
12710         this.addCard({
12711             id : Roo.bootstrap.CardUploader.ID--,
12712             is_uploaded : false,
12713             src : url,
12714             srcfile : file,
12715             title : file.name,
12716             mimetype : file.type,
12717             preview : false,
12718             is_deleted : 0
12719         });
12720         
12721     },
12722     
12723     /**
12724      * addCard - add an Attachment to the uploader
12725      * @param data - the data about the image to upload
12726      *
12727      * {
12728           id : 123
12729           title : "Title of file",
12730           is_uploaded : false,
12731           src : "http://.....",
12732           srcfile : { the File upload object },
12733           mimetype : file.type,
12734           preview : false,
12735           is_deleted : 0
12736           .. any other data...
12737         }
12738      *
12739      * 
12740     */
12741     
12742     addCard : function (data)
12743     {
12744         // hidden input element?
12745         // if the file is not an image...
12746         //then we need to use something other that and header_image
12747         var t = this;
12748         //   remove.....
12749         var footer = [
12750             {
12751                 xns : Roo.bootstrap,
12752                 xtype : 'CardFooter',
12753                  items: [
12754                     {
12755                         xns : Roo.bootstrap,
12756                         xtype : 'Element',
12757                         cls : 'd-flex',
12758                         items : [
12759                             
12760                             {
12761                                 xns : Roo.bootstrap,
12762                                 xtype : 'Button',
12763                                 html : String.format("<small>{0}</small>", data.title),
12764                                 cls : 'col-10 text-left',
12765                                 size: 'sm',
12766                                 weight: 'link',
12767                                 fa : 'download',
12768                                 listeners : {
12769                                     click : function() {
12770                                      
12771                                         t.fireEvent( "download", t, data );
12772                                     }
12773                                 }
12774                             },
12775                           
12776                             {
12777                                 xns : Roo.bootstrap,
12778                                 xtype : 'Button',
12779                                 style: 'max-height: 28px; ',
12780                                 size : 'sm',
12781                                 weight: 'danger',
12782                                 cls : 'col-2',
12783                                 fa : 'times',
12784                                 listeners : {
12785                                     click : function() {
12786                                         t.removeCard(data.id)
12787                                     }
12788                                 }
12789                             }
12790                         ]
12791                     }
12792                     
12793                 ] 
12794             }
12795             
12796         ];
12797         
12798         var cn = this.addxtype(
12799             {
12800                  
12801                 xns : Roo.bootstrap,
12802                 xtype : 'Card',
12803                 closeable : true,
12804                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12805                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12806                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12807                 data : data,
12808                 html : false,
12809                  
12810                 items : footer,
12811                 initEvents : function() {
12812                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12813                     var card = this;
12814                     this.imgEl = this.el.select('.card-img-top').first();
12815                     if (this.imgEl) {
12816                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12817                         this.imgEl.set({ 'pointer' : 'cursor' });
12818                                   
12819                     }
12820                     this.getCardFooter().addClass('p-1');
12821                     
12822                   
12823                 }
12824                 
12825             }
12826         );
12827         // dont' really need ot update items.
12828         // this.items.push(cn);
12829         this.fileCollection.add(cn);
12830         
12831         if (!data.srcfile) {
12832             this.updateInput();
12833             return;
12834         }
12835             
12836         var _t = this;
12837         var reader = new FileReader();
12838         reader.addEventListener("load", function() {  
12839             data.srcdata =  reader.result;
12840             _t.updateInput();
12841         });
12842         reader.readAsDataURL(data.srcfile);
12843         
12844         
12845         
12846     },
12847     removeCard : function(id)
12848     {
12849         
12850         var card  = this.fileCollection.get(id);
12851         card.data.is_deleted = 1;
12852         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12853         //this.fileCollection.remove(card);
12854         //this.items = this.items.filter(function(e) { return e != card });
12855         // dont' really need ot update items.
12856         card.el.dom.parentNode.removeChild(card.el.dom);
12857         this.updateInput();
12858
12859         
12860     },
12861     reset: function()
12862     {
12863         this.fileCollection.each(function(card) {
12864             if (card.el.dom && card.el.dom.parentNode) {
12865                 card.el.dom.parentNode.removeChild(card.el.dom);
12866             }
12867         });
12868         this.fileCollection.clear();
12869         this.updateInput();
12870     },
12871     
12872     updateInput : function()
12873     {
12874          var data = [];
12875         this.fileCollection.each(function(e) {
12876             data.push(e.data);
12877             
12878         });
12879         this.inputEl().dom.value = JSON.stringify(data);
12880         
12881         
12882         
12883     }
12884     
12885     
12886 });
12887
12888
12889 Roo.bootstrap.CardUploader.ID = -1;/*
12890  * Based on:
12891  * Ext JS Library 1.1.1
12892  * Copyright(c) 2006-2007, Ext JS, LLC.
12893  *
12894  * Originally Released Under LGPL - original licence link has changed is not relivant.
12895  *
12896  * Fork - LGPL
12897  * <script type="text/javascript">
12898  */
12899
12900
12901 /**
12902  * @class Roo.data.SortTypes
12903  * @singleton
12904  * Defines the default sorting (casting?) comparison functions used when sorting data.
12905  */
12906 Roo.data.SortTypes = {
12907     /**
12908      * Default sort that does nothing
12909      * @param {Mixed} s The value being converted
12910      * @return {Mixed} The comparison value
12911      */
12912     none : function(s){
12913         return s;
12914     },
12915     
12916     /**
12917      * The regular expression used to strip tags
12918      * @type {RegExp}
12919      * @property
12920      */
12921     stripTagsRE : /<\/?[^>]+>/gi,
12922     
12923     /**
12924      * Strips all HTML tags to sort on text only
12925      * @param {Mixed} s The value being converted
12926      * @return {String} The comparison value
12927      */
12928     asText : function(s){
12929         return String(s).replace(this.stripTagsRE, "");
12930     },
12931     
12932     /**
12933      * Strips all HTML tags to sort on text only - Case insensitive
12934      * @param {Mixed} s The value being converted
12935      * @return {String} The comparison value
12936      */
12937     asUCText : function(s){
12938         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12939     },
12940     
12941     /**
12942      * Case insensitive string
12943      * @param {Mixed} s The value being converted
12944      * @return {String} The comparison value
12945      */
12946     asUCString : function(s) {
12947         return String(s).toUpperCase();
12948     },
12949     
12950     /**
12951      * Date sorting
12952      * @param {Mixed} s The value being converted
12953      * @return {Number} The comparison value
12954      */
12955     asDate : function(s) {
12956         if(!s){
12957             return 0;
12958         }
12959         if(s instanceof Date){
12960             return s.getTime();
12961         }
12962         return Date.parse(String(s));
12963     },
12964     
12965     /**
12966      * Float sorting
12967      * @param {Mixed} s The value being converted
12968      * @return {Float} The comparison value
12969      */
12970     asFloat : function(s) {
12971         var val = parseFloat(String(s).replace(/,/g, ""));
12972         if(isNaN(val)) {
12973             val = 0;
12974         }
12975         return val;
12976     },
12977     
12978     /**
12979      * Integer sorting
12980      * @param {Mixed} s The value being converted
12981      * @return {Number} The comparison value
12982      */
12983     asInt : function(s) {
12984         var val = parseInt(String(s).replace(/,/g, ""));
12985         if(isNaN(val)) {
12986             val = 0;
12987         }
12988         return val;
12989     }
12990 };/*
12991  * Based on:
12992  * Ext JS Library 1.1.1
12993  * Copyright(c) 2006-2007, Ext JS, LLC.
12994  *
12995  * Originally Released Under LGPL - original licence link has changed is not relivant.
12996  *
12997  * Fork - LGPL
12998  * <script type="text/javascript">
12999  */
13000
13001 /**
13002 * @class Roo.data.Record
13003  * Instances of this class encapsulate both record <em>definition</em> information, and record
13004  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13005  * to access Records cached in an {@link Roo.data.Store} object.<br>
13006  * <p>
13007  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13008  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13009  * objects.<br>
13010  * <p>
13011  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13012  * @constructor
13013  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13014  * {@link #create}. The parameters are the same.
13015  * @param {Array} data An associative Array of data values keyed by the field name.
13016  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13017  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13018  * not specified an integer id is generated.
13019  */
13020 Roo.data.Record = function(data, id){
13021     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13022     this.data = data;
13023 };
13024
13025 /**
13026  * Generate a constructor for a specific record layout.
13027  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13028  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13029  * Each field definition object may contain the following properties: <ul>
13030  * <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,
13031  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13032  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13033  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13034  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13035  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13036  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13037  * this may be omitted.</p></li>
13038  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13039  * <ul><li>auto (Default, implies no conversion)</li>
13040  * <li>string</li>
13041  * <li>int</li>
13042  * <li>float</li>
13043  * <li>boolean</li>
13044  * <li>date</li></ul></p></li>
13045  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13046  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13047  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13048  * by the Reader into an object that will be stored in the Record. It is passed the
13049  * following parameters:<ul>
13050  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13051  * </ul></p></li>
13052  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13053  * </ul>
13054  * <br>usage:<br><pre><code>
13055 var TopicRecord = Roo.data.Record.create(
13056     {name: 'title', mapping: 'topic_title'},
13057     {name: 'author', mapping: 'username'},
13058     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13059     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13060     {name: 'lastPoster', mapping: 'user2'},
13061     {name: 'excerpt', mapping: 'post_text'}
13062 );
13063
13064 var myNewRecord = new TopicRecord({
13065     title: 'Do my job please',
13066     author: 'noobie',
13067     totalPosts: 1,
13068     lastPost: new Date(),
13069     lastPoster: 'Animal',
13070     excerpt: 'No way dude!'
13071 });
13072 myStore.add(myNewRecord);
13073 </code></pre>
13074  * @method create
13075  * @static
13076  */
13077 Roo.data.Record.create = function(o){
13078     var f = function(){
13079         f.superclass.constructor.apply(this, arguments);
13080     };
13081     Roo.extend(f, Roo.data.Record);
13082     var p = f.prototype;
13083     p.fields = new Roo.util.MixedCollection(false, function(field){
13084         return field.name;
13085     });
13086     for(var i = 0, len = o.length; i < len; i++){
13087         p.fields.add(new Roo.data.Field(o[i]));
13088     }
13089     f.getField = function(name){
13090         return p.fields.get(name);  
13091     };
13092     return f;
13093 };
13094
13095 Roo.data.Record.AUTO_ID = 1000;
13096 Roo.data.Record.EDIT = 'edit';
13097 Roo.data.Record.REJECT = 'reject';
13098 Roo.data.Record.COMMIT = 'commit';
13099
13100 Roo.data.Record.prototype = {
13101     /**
13102      * Readonly flag - true if this record has been modified.
13103      * @type Boolean
13104      */
13105     dirty : false,
13106     editing : false,
13107     error: null,
13108     modified: null,
13109
13110     // private
13111     join : function(store){
13112         this.store = store;
13113     },
13114
13115     /**
13116      * Set the named field to the specified value.
13117      * @param {String} name The name of the field to set.
13118      * @param {Object} value The value to set the field to.
13119      */
13120     set : function(name, value){
13121         if(this.data[name] == value){
13122             return;
13123         }
13124         this.dirty = true;
13125         if(!this.modified){
13126             this.modified = {};
13127         }
13128         if(typeof this.modified[name] == 'undefined'){
13129             this.modified[name] = this.data[name];
13130         }
13131         this.data[name] = value;
13132         if(!this.editing && this.store){
13133             this.store.afterEdit(this);
13134         }       
13135     },
13136
13137     /**
13138      * Get the value of the named field.
13139      * @param {String} name The name of the field to get the value of.
13140      * @return {Object} The value of the field.
13141      */
13142     get : function(name){
13143         return this.data[name]; 
13144     },
13145
13146     // private
13147     beginEdit : function(){
13148         this.editing = true;
13149         this.modified = {}; 
13150     },
13151
13152     // private
13153     cancelEdit : function(){
13154         this.editing = false;
13155         delete this.modified;
13156     },
13157
13158     // private
13159     endEdit : function(){
13160         this.editing = false;
13161         if(this.dirty && this.store){
13162             this.store.afterEdit(this);
13163         }
13164     },
13165
13166     /**
13167      * Usually called by the {@link Roo.data.Store} which owns the Record.
13168      * Rejects all changes made to the Record since either creation, or the last commit operation.
13169      * Modified fields are reverted to their original values.
13170      * <p>
13171      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13172      * of reject operations.
13173      */
13174     reject : function(){
13175         var m = this.modified;
13176         for(var n in m){
13177             if(typeof m[n] != "function"){
13178                 this.data[n] = m[n];
13179             }
13180         }
13181         this.dirty = false;
13182         delete this.modified;
13183         this.editing = false;
13184         if(this.store){
13185             this.store.afterReject(this);
13186         }
13187     },
13188
13189     /**
13190      * Usually called by the {@link Roo.data.Store} which owns the Record.
13191      * Commits all changes made to the Record since either creation, or the last commit operation.
13192      * <p>
13193      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13194      * of commit operations.
13195      */
13196     commit : function(){
13197         this.dirty = false;
13198         delete this.modified;
13199         this.editing = false;
13200         if(this.store){
13201             this.store.afterCommit(this);
13202         }
13203     },
13204
13205     // private
13206     hasError : function(){
13207         return this.error != null;
13208     },
13209
13210     // private
13211     clearError : function(){
13212         this.error = null;
13213     },
13214
13215     /**
13216      * Creates a copy of this record.
13217      * @param {String} id (optional) A new record id if you don't want to use this record's id
13218      * @return {Record}
13219      */
13220     copy : function(newId) {
13221         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13222     }
13223 };/*
13224  * Based on:
13225  * Ext JS Library 1.1.1
13226  * Copyright(c) 2006-2007, Ext JS, LLC.
13227  *
13228  * Originally Released Under LGPL - original licence link has changed is not relivant.
13229  *
13230  * Fork - LGPL
13231  * <script type="text/javascript">
13232  */
13233
13234
13235
13236 /**
13237  * @class Roo.data.Store
13238  * @extends Roo.util.Observable
13239  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13240  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13241  * <p>
13242  * 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
13243  * has no knowledge of the format of the data returned by the Proxy.<br>
13244  * <p>
13245  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13246  * instances from the data object. These records are cached and made available through accessor functions.
13247  * @constructor
13248  * Creates a new Store.
13249  * @param {Object} config A config object containing the objects needed for the Store to access data,
13250  * and read the data into Records.
13251  */
13252 Roo.data.Store = function(config){
13253     this.data = new Roo.util.MixedCollection(false);
13254     this.data.getKey = function(o){
13255         return o.id;
13256     };
13257     this.baseParams = {};
13258     // private
13259     this.paramNames = {
13260         "start" : "start",
13261         "limit" : "limit",
13262         "sort" : "sort",
13263         "dir" : "dir",
13264         "multisort" : "_multisort"
13265     };
13266
13267     if(config && config.data){
13268         this.inlineData = config.data;
13269         delete config.data;
13270     }
13271
13272     Roo.apply(this, config);
13273     
13274     if(this.reader){ // reader passed
13275         this.reader = Roo.factory(this.reader, Roo.data);
13276         this.reader.xmodule = this.xmodule || false;
13277         if(!this.recordType){
13278             this.recordType = this.reader.recordType;
13279         }
13280         if(this.reader.onMetaChange){
13281             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13282         }
13283     }
13284
13285     if(this.recordType){
13286         this.fields = this.recordType.prototype.fields;
13287     }
13288     this.modified = [];
13289
13290     this.addEvents({
13291         /**
13292          * @event datachanged
13293          * Fires when the data cache has changed, and a widget which is using this Store
13294          * as a Record cache should refresh its view.
13295          * @param {Store} this
13296          */
13297         datachanged : true,
13298         /**
13299          * @event metachange
13300          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13301          * @param {Store} this
13302          * @param {Object} meta The JSON metadata
13303          */
13304         metachange : true,
13305         /**
13306          * @event add
13307          * Fires when Records have been added to the Store
13308          * @param {Store} this
13309          * @param {Roo.data.Record[]} records The array of Records added
13310          * @param {Number} index The index at which the record(s) were added
13311          */
13312         add : true,
13313         /**
13314          * @event remove
13315          * Fires when a Record has been removed from the Store
13316          * @param {Store} this
13317          * @param {Roo.data.Record} record The Record that was removed
13318          * @param {Number} index The index at which the record was removed
13319          */
13320         remove : true,
13321         /**
13322          * @event update
13323          * Fires when a Record has been updated
13324          * @param {Store} this
13325          * @param {Roo.data.Record} record The Record that was updated
13326          * @param {String} operation The update operation being performed.  Value may be one of:
13327          * <pre><code>
13328  Roo.data.Record.EDIT
13329  Roo.data.Record.REJECT
13330  Roo.data.Record.COMMIT
13331          * </code></pre>
13332          */
13333         update : true,
13334         /**
13335          * @event clear
13336          * Fires when the data cache has been cleared.
13337          * @param {Store} this
13338          */
13339         clear : true,
13340         /**
13341          * @event beforeload
13342          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13343          * the load action will be canceled.
13344          * @param {Store} this
13345          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13346          */
13347         beforeload : true,
13348         /**
13349          * @event beforeloadadd
13350          * Fires after a new set of Records has been loaded.
13351          * @param {Store} this
13352          * @param {Roo.data.Record[]} records The Records that were loaded
13353          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13354          */
13355         beforeloadadd : true,
13356         /**
13357          * @event load
13358          * Fires after a new set of Records has been loaded, before they are added to the store.
13359          * @param {Store} this
13360          * @param {Roo.data.Record[]} records The Records that were loaded
13361          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13362          * @params {Object} return from reader
13363          */
13364         load : true,
13365         /**
13366          * @event loadexception
13367          * Fires if an exception occurs in the Proxy during loading.
13368          * Called with the signature of the Proxy's "loadexception" event.
13369          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13370          * 
13371          * @param {Proxy} 
13372          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13373          * @param {Object} load options 
13374          * @param {Object} jsonData from your request (normally this contains the Exception)
13375          */
13376         loadexception : true
13377     });
13378     
13379     if(this.proxy){
13380         this.proxy = Roo.factory(this.proxy, Roo.data);
13381         this.proxy.xmodule = this.xmodule || false;
13382         this.relayEvents(this.proxy,  ["loadexception"]);
13383     }
13384     this.sortToggle = {};
13385     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13386
13387     Roo.data.Store.superclass.constructor.call(this);
13388
13389     if(this.inlineData){
13390         this.loadData(this.inlineData);
13391         delete this.inlineData;
13392     }
13393 };
13394
13395 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13396      /**
13397     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13398     * without a remote query - used by combo/forms at present.
13399     */
13400     
13401     /**
13402     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13403     */
13404     /**
13405     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13406     */
13407     /**
13408     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13409     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13410     */
13411     /**
13412     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13413     * on any HTTP request
13414     */
13415     /**
13416     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13417     */
13418     /**
13419     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13420     */
13421     multiSort: false,
13422     /**
13423     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13424     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13425     */
13426     remoteSort : false,
13427
13428     /**
13429     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13430      * loaded or when a record is removed. (defaults to false).
13431     */
13432     pruneModifiedRecords : false,
13433
13434     // private
13435     lastOptions : null,
13436
13437     /**
13438      * Add Records to the Store and fires the add event.
13439      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13440      */
13441     add : function(records){
13442         records = [].concat(records);
13443         for(var i = 0, len = records.length; i < len; i++){
13444             records[i].join(this);
13445         }
13446         var index = this.data.length;
13447         this.data.addAll(records);
13448         this.fireEvent("add", this, records, index);
13449     },
13450
13451     /**
13452      * Remove a Record from the Store and fires the remove event.
13453      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13454      */
13455     remove : function(record){
13456         var index = this.data.indexOf(record);
13457         this.data.removeAt(index);
13458  
13459         if(this.pruneModifiedRecords){
13460             this.modified.remove(record);
13461         }
13462         this.fireEvent("remove", this, record, index);
13463     },
13464
13465     /**
13466      * Remove all Records from the Store and fires the clear event.
13467      */
13468     removeAll : function(){
13469         this.data.clear();
13470         if(this.pruneModifiedRecords){
13471             this.modified = [];
13472         }
13473         this.fireEvent("clear", this);
13474     },
13475
13476     /**
13477      * Inserts Records to the Store at the given index and fires the add event.
13478      * @param {Number} index The start index at which to insert the passed Records.
13479      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13480      */
13481     insert : function(index, records){
13482         records = [].concat(records);
13483         for(var i = 0, len = records.length; i < len; i++){
13484             this.data.insert(index, records[i]);
13485             records[i].join(this);
13486         }
13487         this.fireEvent("add", this, records, index);
13488     },
13489
13490     /**
13491      * Get the index within the cache of the passed Record.
13492      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13493      * @return {Number} The index of the passed Record. Returns -1 if not found.
13494      */
13495     indexOf : function(record){
13496         return this.data.indexOf(record);
13497     },
13498
13499     /**
13500      * Get the index within the cache of the Record with the passed id.
13501      * @param {String} id The id of the Record to find.
13502      * @return {Number} The index of the Record. Returns -1 if not found.
13503      */
13504     indexOfId : function(id){
13505         return this.data.indexOfKey(id);
13506     },
13507
13508     /**
13509      * Get the Record with the specified id.
13510      * @param {String} id The id of the Record to find.
13511      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13512      */
13513     getById : function(id){
13514         return this.data.key(id);
13515     },
13516
13517     /**
13518      * Get the Record at the specified index.
13519      * @param {Number} index The index of the Record to find.
13520      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13521      */
13522     getAt : function(index){
13523         return this.data.itemAt(index);
13524     },
13525
13526     /**
13527      * Returns a range of Records between specified indices.
13528      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13529      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13530      * @return {Roo.data.Record[]} An array of Records
13531      */
13532     getRange : function(start, end){
13533         return this.data.getRange(start, end);
13534     },
13535
13536     // private
13537     storeOptions : function(o){
13538         o = Roo.apply({}, o);
13539         delete o.callback;
13540         delete o.scope;
13541         this.lastOptions = o;
13542     },
13543
13544     /**
13545      * Loads the Record cache from the configured Proxy using the configured Reader.
13546      * <p>
13547      * If using remote paging, then the first load call must specify the <em>start</em>
13548      * and <em>limit</em> properties in the options.params property to establish the initial
13549      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13550      * <p>
13551      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13552      * and this call will return before the new data has been loaded. Perform any post-processing
13553      * in a callback function, or in a "load" event handler.</strong>
13554      * <p>
13555      * @param {Object} options An object containing properties which control loading options:<ul>
13556      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13557      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13558      * passed the following arguments:<ul>
13559      * <li>r : Roo.data.Record[]</li>
13560      * <li>options: Options object from the load call</li>
13561      * <li>success: Boolean success indicator</li></ul></li>
13562      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13563      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13564      * </ul>
13565      */
13566     load : function(options){
13567         options = options || {};
13568         if(this.fireEvent("beforeload", this, options) !== false){
13569             this.storeOptions(options);
13570             var p = Roo.apply(options.params || {}, this.baseParams);
13571             // if meta was not loaded from remote source.. try requesting it.
13572             if (!this.reader.metaFromRemote) {
13573                 p._requestMeta = 1;
13574             }
13575             if(this.sortInfo && this.remoteSort){
13576                 var pn = this.paramNames;
13577                 p[pn["sort"]] = this.sortInfo.field;
13578                 p[pn["dir"]] = this.sortInfo.direction;
13579             }
13580             if (this.multiSort) {
13581                 var pn = this.paramNames;
13582                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13583             }
13584             
13585             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13586         }
13587     },
13588
13589     /**
13590      * Reloads the Record cache from the configured Proxy using the configured Reader and
13591      * the options from the last load operation performed.
13592      * @param {Object} options (optional) An object containing properties which may override the options
13593      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13594      * the most recently used options are reused).
13595      */
13596     reload : function(options){
13597         this.load(Roo.applyIf(options||{}, this.lastOptions));
13598     },
13599
13600     // private
13601     // Called as a callback by the Reader during a load operation.
13602     loadRecords : function(o, options, success){
13603         if(!o || success === false){
13604             if(success !== false){
13605                 this.fireEvent("load", this, [], options, o);
13606             }
13607             if(options.callback){
13608                 options.callback.call(options.scope || this, [], options, false);
13609             }
13610             return;
13611         }
13612         // if data returned failure - throw an exception.
13613         if (o.success === false) {
13614             // show a message if no listener is registered.
13615             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13616                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13617             }
13618             // loadmask wil be hooked into this..
13619             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13620             return;
13621         }
13622         var r = o.records, t = o.totalRecords || r.length;
13623         
13624         this.fireEvent("beforeloadadd", this, r, options, o);
13625         
13626         if(!options || options.add !== true){
13627             if(this.pruneModifiedRecords){
13628                 this.modified = [];
13629             }
13630             for(var i = 0, len = r.length; i < len; i++){
13631                 r[i].join(this);
13632             }
13633             if(this.snapshot){
13634                 this.data = this.snapshot;
13635                 delete this.snapshot;
13636             }
13637             this.data.clear();
13638             this.data.addAll(r);
13639             this.totalLength = t;
13640             this.applySort();
13641             this.fireEvent("datachanged", this);
13642         }else{
13643             this.totalLength = Math.max(t, this.data.length+r.length);
13644             this.add(r);
13645         }
13646         
13647         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13648                 
13649             var e = new Roo.data.Record({});
13650
13651             e.set(this.parent.displayField, this.parent.emptyTitle);
13652             e.set(this.parent.valueField, '');
13653
13654             this.insert(0, e);
13655         }
13656             
13657         this.fireEvent("load", this, r, options, o);
13658         if(options.callback){
13659             options.callback.call(options.scope || this, r, options, true);
13660         }
13661     },
13662
13663
13664     /**
13665      * Loads data from a passed data block. A Reader which understands the format of the data
13666      * must have been configured in the constructor.
13667      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13668      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13669      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13670      */
13671     loadData : function(o, append){
13672         var r = this.reader.readRecords(o);
13673         this.loadRecords(r, {add: append}, true);
13674     },
13675     
13676      /**
13677      * using 'cn' the nested child reader read the child array into it's child stores.
13678      * @param {Object} rec The record with a 'children array
13679      */
13680     loadDataFromChildren : function(rec)
13681     {
13682         this.loadData(this.reader.toLoadData(rec));
13683     },
13684     
13685
13686     /**
13687      * Gets the number of cached records.
13688      * <p>
13689      * <em>If using paging, this may not be the total size of the dataset. If the data object
13690      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13691      * the data set size</em>
13692      */
13693     getCount : function(){
13694         return this.data.length || 0;
13695     },
13696
13697     /**
13698      * Gets the total number of records in the dataset as returned by the server.
13699      * <p>
13700      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13701      * the dataset size</em>
13702      */
13703     getTotalCount : function(){
13704         return this.totalLength || 0;
13705     },
13706
13707     /**
13708      * Returns the sort state of the Store as an object with two properties:
13709      * <pre><code>
13710  field {String} The name of the field by which the Records are sorted
13711  direction {String} The sort order, "ASC" or "DESC"
13712      * </code></pre>
13713      */
13714     getSortState : function(){
13715         return this.sortInfo;
13716     },
13717
13718     // private
13719     applySort : function(){
13720         if(this.sortInfo && !this.remoteSort){
13721             var s = this.sortInfo, f = s.field;
13722             var st = this.fields.get(f).sortType;
13723             var fn = function(r1, r2){
13724                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13725                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13726             };
13727             this.data.sort(s.direction, fn);
13728             if(this.snapshot && this.snapshot != this.data){
13729                 this.snapshot.sort(s.direction, fn);
13730             }
13731         }
13732     },
13733
13734     /**
13735      * Sets the default sort column and order to be used by the next load operation.
13736      * @param {String} fieldName The name of the field to sort by.
13737      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13738      */
13739     setDefaultSort : function(field, dir){
13740         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13741     },
13742
13743     /**
13744      * Sort the Records.
13745      * If remote sorting is used, the sort is performed on the server, and the cache is
13746      * reloaded. If local sorting is used, the cache is sorted internally.
13747      * @param {String} fieldName The name of the field to sort by.
13748      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13749      */
13750     sort : function(fieldName, dir){
13751         var f = this.fields.get(fieldName);
13752         if(!dir){
13753             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13754             
13755             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13756                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13757             }else{
13758                 dir = f.sortDir;
13759             }
13760         }
13761         this.sortToggle[f.name] = dir;
13762         this.sortInfo = {field: f.name, direction: dir};
13763         if(!this.remoteSort){
13764             this.applySort();
13765             this.fireEvent("datachanged", this);
13766         }else{
13767             this.load(this.lastOptions);
13768         }
13769     },
13770
13771     /**
13772      * Calls the specified function for each of the Records in the cache.
13773      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13774      * Returning <em>false</em> aborts and exits the iteration.
13775      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13776      */
13777     each : function(fn, scope){
13778         this.data.each(fn, scope);
13779     },
13780
13781     /**
13782      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13783      * (e.g., during paging).
13784      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13785      */
13786     getModifiedRecords : function(){
13787         return this.modified;
13788     },
13789
13790     // private
13791     createFilterFn : function(property, value, anyMatch){
13792         if(!value.exec){ // not a regex
13793             value = String(value);
13794             if(value.length == 0){
13795                 return false;
13796             }
13797             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13798         }
13799         return function(r){
13800             return value.test(r.data[property]);
13801         };
13802     },
13803
13804     /**
13805      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13806      * @param {String} property A field on your records
13807      * @param {Number} start The record index to start at (defaults to 0)
13808      * @param {Number} end The last record index to include (defaults to length - 1)
13809      * @return {Number} The sum
13810      */
13811     sum : function(property, start, end){
13812         var rs = this.data.items, v = 0;
13813         start = start || 0;
13814         end = (end || end === 0) ? end : rs.length-1;
13815
13816         for(var i = start; i <= end; i++){
13817             v += (rs[i].data[property] || 0);
13818         }
13819         return v;
13820     },
13821
13822     /**
13823      * Filter the records by a specified property.
13824      * @param {String} field A field on your records
13825      * @param {String/RegExp} value Either a string that the field
13826      * should start with or a RegExp to test against the field
13827      * @param {Boolean} anyMatch True to match any part not just the beginning
13828      */
13829     filter : function(property, value, anyMatch){
13830         var fn = this.createFilterFn(property, value, anyMatch);
13831         return fn ? this.filterBy(fn) : this.clearFilter();
13832     },
13833
13834     /**
13835      * Filter by a function. The specified function will be called with each
13836      * record in this data source. If the function returns true the record is included,
13837      * otherwise it is filtered.
13838      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13839      * @param {Object} scope (optional) The scope of the function (defaults to this)
13840      */
13841     filterBy : function(fn, scope){
13842         this.snapshot = this.snapshot || this.data;
13843         this.data = this.queryBy(fn, scope||this);
13844         this.fireEvent("datachanged", this);
13845     },
13846
13847     /**
13848      * Query the records by a specified property.
13849      * @param {String} field A field on your records
13850      * @param {String/RegExp} value Either a string that the field
13851      * should start with or a RegExp to test against the field
13852      * @param {Boolean} anyMatch True to match any part not just the beginning
13853      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13854      */
13855     query : function(property, value, anyMatch){
13856         var fn = this.createFilterFn(property, value, anyMatch);
13857         return fn ? this.queryBy(fn) : this.data.clone();
13858     },
13859
13860     /**
13861      * Query by a function. The specified function will be called with each
13862      * record in this data source. If the function returns true the record is included
13863      * in the results.
13864      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13865      * @param {Object} scope (optional) The scope of the function (defaults to this)
13866       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13867      **/
13868     queryBy : function(fn, scope){
13869         var data = this.snapshot || this.data;
13870         return data.filterBy(fn, scope||this);
13871     },
13872
13873     /**
13874      * Collects unique values for a particular dataIndex from this store.
13875      * @param {String} dataIndex The property to collect
13876      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13877      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13878      * @return {Array} An array of the unique values
13879      **/
13880     collect : function(dataIndex, allowNull, bypassFilter){
13881         var d = (bypassFilter === true && this.snapshot) ?
13882                 this.snapshot.items : this.data.items;
13883         var v, sv, r = [], l = {};
13884         for(var i = 0, len = d.length; i < len; i++){
13885             v = d[i].data[dataIndex];
13886             sv = String(v);
13887             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13888                 l[sv] = true;
13889                 r[r.length] = v;
13890             }
13891         }
13892         return r;
13893     },
13894
13895     /**
13896      * Revert to a view of the Record cache with no filtering applied.
13897      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13898      */
13899     clearFilter : function(suppressEvent){
13900         if(this.snapshot && this.snapshot != this.data){
13901             this.data = this.snapshot;
13902             delete this.snapshot;
13903             if(suppressEvent !== true){
13904                 this.fireEvent("datachanged", this);
13905             }
13906         }
13907     },
13908
13909     // private
13910     afterEdit : function(record){
13911         if(this.modified.indexOf(record) == -1){
13912             this.modified.push(record);
13913         }
13914         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13915     },
13916     
13917     // private
13918     afterReject : function(record){
13919         this.modified.remove(record);
13920         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13921     },
13922
13923     // private
13924     afterCommit : function(record){
13925         this.modified.remove(record);
13926         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13927     },
13928
13929     /**
13930      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13931      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13932      */
13933     commitChanges : function(){
13934         var m = this.modified.slice(0);
13935         this.modified = [];
13936         for(var i = 0, len = m.length; i < len; i++){
13937             m[i].commit();
13938         }
13939     },
13940
13941     /**
13942      * Cancel outstanding changes on all changed records.
13943      */
13944     rejectChanges : function(){
13945         var m = this.modified.slice(0);
13946         this.modified = [];
13947         for(var i = 0, len = m.length; i < len; i++){
13948             m[i].reject();
13949         }
13950     },
13951
13952     onMetaChange : function(meta, rtype, o){
13953         this.recordType = rtype;
13954         this.fields = rtype.prototype.fields;
13955         delete this.snapshot;
13956         this.sortInfo = meta.sortInfo || this.sortInfo;
13957         this.modified = [];
13958         this.fireEvent('metachange', this, this.reader.meta);
13959     },
13960     
13961     moveIndex : function(data, type)
13962     {
13963         var index = this.indexOf(data);
13964         
13965         var newIndex = index + type;
13966         
13967         this.remove(data);
13968         
13969         this.insert(newIndex, data);
13970         
13971     }
13972 });/*
13973  * Based on:
13974  * Ext JS Library 1.1.1
13975  * Copyright(c) 2006-2007, Ext JS, LLC.
13976  *
13977  * Originally Released Under LGPL - original licence link has changed is not relivant.
13978  *
13979  * Fork - LGPL
13980  * <script type="text/javascript">
13981  */
13982
13983 /**
13984  * @class Roo.data.SimpleStore
13985  * @extends Roo.data.Store
13986  * Small helper class to make creating Stores from Array data easier.
13987  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13988  * @cfg {Array} fields An array of field definition objects, or field name strings.
13989  * @cfg {Object} an existing reader (eg. copied from another store)
13990  * @cfg {Array} data The multi-dimensional array of data
13991  * @constructor
13992  * @param {Object} config
13993  */
13994 Roo.data.SimpleStore = function(config)
13995 {
13996     Roo.data.SimpleStore.superclass.constructor.call(this, {
13997         isLocal : true,
13998         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13999                 id: config.id
14000             },
14001             Roo.data.Record.create(config.fields)
14002         ),
14003         proxy : new Roo.data.MemoryProxy(config.data)
14004     });
14005     this.load();
14006 };
14007 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14008  * Based on:
14009  * Ext JS Library 1.1.1
14010  * Copyright(c) 2006-2007, Ext JS, LLC.
14011  *
14012  * Originally Released Under LGPL - original licence link has changed is not relivant.
14013  *
14014  * Fork - LGPL
14015  * <script type="text/javascript">
14016  */
14017
14018 /**
14019 /**
14020  * @extends Roo.data.Store
14021  * @class Roo.data.JsonStore
14022  * Small helper class to make creating Stores for JSON data easier. <br/>
14023 <pre><code>
14024 var store = new Roo.data.JsonStore({
14025     url: 'get-images.php',
14026     root: 'images',
14027     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14028 });
14029 </code></pre>
14030  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14031  * JsonReader and HttpProxy (unless inline data is provided).</b>
14032  * @cfg {Array} fields An array of field definition objects, or field name strings.
14033  * @constructor
14034  * @param {Object} config
14035  */
14036 Roo.data.JsonStore = function(c){
14037     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14038         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14039         reader: new Roo.data.JsonReader(c, c.fields)
14040     }));
14041 };
14042 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14043  * Based on:
14044  * Ext JS Library 1.1.1
14045  * Copyright(c) 2006-2007, Ext JS, LLC.
14046  *
14047  * Originally Released Under LGPL - original licence link has changed is not relivant.
14048  *
14049  * Fork - LGPL
14050  * <script type="text/javascript">
14051  */
14052
14053  
14054 Roo.data.Field = function(config){
14055     if(typeof config == "string"){
14056         config = {name: config};
14057     }
14058     Roo.apply(this, config);
14059     
14060     if(!this.type){
14061         this.type = "auto";
14062     }
14063     
14064     var st = Roo.data.SortTypes;
14065     // named sortTypes are supported, here we look them up
14066     if(typeof this.sortType == "string"){
14067         this.sortType = st[this.sortType];
14068     }
14069     
14070     // set default sortType for strings and dates
14071     if(!this.sortType){
14072         switch(this.type){
14073             case "string":
14074                 this.sortType = st.asUCString;
14075                 break;
14076             case "date":
14077                 this.sortType = st.asDate;
14078                 break;
14079             default:
14080                 this.sortType = st.none;
14081         }
14082     }
14083
14084     // define once
14085     var stripRe = /[\$,%]/g;
14086
14087     // prebuilt conversion function for this field, instead of
14088     // switching every time we're reading a value
14089     if(!this.convert){
14090         var cv, dateFormat = this.dateFormat;
14091         switch(this.type){
14092             case "":
14093             case "auto":
14094             case undefined:
14095                 cv = function(v){ return v; };
14096                 break;
14097             case "string":
14098                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14099                 break;
14100             case "int":
14101                 cv = function(v){
14102                     return v !== undefined && v !== null && v !== '' ?
14103                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14104                     };
14105                 break;
14106             case "float":
14107                 cv = function(v){
14108                     return v !== undefined && v !== null && v !== '' ?
14109                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14110                     };
14111                 break;
14112             case "bool":
14113             case "boolean":
14114                 cv = function(v){ return v === true || v === "true" || v == 1; };
14115                 break;
14116             case "date":
14117                 cv = function(v){
14118                     if(!v){
14119                         return '';
14120                     }
14121                     if(v instanceof Date){
14122                         return v;
14123                     }
14124                     if(dateFormat){
14125                         if(dateFormat == "timestamp"){
14126                             return new Date(v*1000);
14127                         }
14128                         return Date.parseDate(v, dateFormat);
14129                     }
14130                     var parsed = Date.parse(v);
14131                     return parsed ? new Date(parsed) : null;
14132                 };
14133              break;
14134             
14135         }
14136         this.convert = cv;
14137     }
14138 };
14139
14140 Roo.data.Field.prototype = {
14141     dateFormat: null,
14142     defaultValue: "",
14143     mapping: null,
14144     sortType : null,
14145     sortDir : "ASC"
14146 };/*
14147  * Based on:
14148  * Ext JS Library 1.1.1
14149  * Copyright(c) 2006-2007, Ext JS, LLC.
14150  *
14151  * Originally Released Under LGPL - original licence link has changed is not relivant.
14152  *
14153  * Fork - LGPL
14154  * <script type="text/javascript">
14155  */
14156  
14157 // Base class for reading structured data from a data source.  This class is intended to be
14158 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14159
14160 /**
14161  * @class Roo.data.DataReader
14162  * Base class for reading structured data from a data source.  This class is intended to be
14163  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14164  */
14165
14166 Roo.data.DataReader = function(meta, recordType){
14167     
14168     this.meta = meta;
14169     
14170     this.recordType = recordType instanceof Array ? 
14171         Roo.data.Record.create(recordType) : recordType;
14172 };
14173
14174 Roo.data.DataReader.prototype = {
14175     
14176     
14177     readerType : 'Data',
14178      /**
14179      * Create an empty record
14180      * @param {Object} data (optional) - overlay some values
14181      * @return {Roo.data.Record} record created.
14182      */
14183     newRow :  function(d) {
14184         var da =  {};
14185         this.recordType.prototype.fields.each(function(c) {
14186             switch( c.type) {
14187                 case 'int' : da[c.name] = 0; break;
14188                 case 'date' : da[c.name] = new Date(); break;
14189                 case 'float' : da[c.name] = 0.0; break;
14190                 case 'boolean' : da[c.name] = false; break;
14191                 default : da[c.name] = ""; break;
14192             }
14193             
14194         });
14195         return new this.recordType(Roo.apply(da, d));
14196     }
14197     
14198     
14199 };/*
14200  * Based on:
14201  * Ext JS Library 1.1.1
14202  * Copyright(c) 2006-2007, Ext JS, LLC.
14203  *
14204  * Originally Released Under LGPL - original licence link has changed is not relivant.
14205  *
14206  * Fork - LGPL
14207  * <script type="text/javascript">
14208  */
14209
14210 /**
14211  * @class Roo.data.DataProxy
14212  * @extends Roo.data.Observable
14213  * This class is an abstract base class for implementations which provide retrieval of
14214  * unformatted data objects.<br>
14215  * <p>
14216  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14217  * (of the appropriate type which knows how to parse the data object) to provide a block of
14218  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14219  * <p>
14220  * Custom implementations must implement the load method as described in
14221  * {@link Roo.data.HttpProxy#load}.
14222  */
14223 Roo.data.DataProxy = function(){
14224     this.addEvents({
14225         /**
14226          * @event beforeload
14227          * Fires before a network request is made to retrieve a data object.
14228          * @param {Object} This DataProxy object.
14229          * @param {Object} params The params parameter to the load function.
14230          */
14231         beforeload : true,
14232         /**
14233          * @event load
14234          * Fires before the load method's callback is called.
14235          * @param {Object} This DataProxy object.
14236          * @param {Object} o The data object.
14237          * @param {Object} arg The callback argument object passed to the load function.
14238          */
14239         load : true,
14240         /**
14241          * @event loadexception
14242          * Fires if an Exception occurs during data retrieval.
14243          * @param {Object} This DataProxy object.
14244          * @param {Object} o The data object.
14245          * @param {Object} arg The callback argument object passed to the load function.
14246          * @param {Object} e The Exception.
14247          */
14248         loadexception : true
14249     });
14250     Roo.data.DataProxy.superclass.constructor.call(this);
14251 };
14252
14253 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14254
14255     /**
14256      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14257      */
14258 /*
14259  * Based on:
14260  * Ext JS Library 1.1.1
14261  * Copyright(c) 2006-2007, Ext JS, LLC.
14262  *
14263  * Originally Released Under LGPL - original licence link has changed is not relivant.
14264  *
14265  * Fork - LGPL
14266  * <script type="text/javascript">
14267  */
14268 /**
14269  * @class Roo.data.MemoryProxy
14270  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14271  * to the Reader when its load method is called.
14272  * @constructor
14273  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14274  */
14275 Roo.data.MemoryProxy = function(data){
14276     if (data.data) {
14277         data = data.data;
14278     }
14279     Roo.data.MemoryProxy.superclass.constructor.call(this);
14280     this.data = data;
14281 };
14282
14283 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14284     
14285     /**
14286      * Load data from the requested source (in this case an in-memory
14287      * data object passed to the constructor), read the data object into
14288      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14289      * process that block using the passed callback.
14290      * @param {Object} params This parameter is not used by the MemoryProxy class.
14291      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14292      * object into a block of Roo.data.Records.
14293      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14294      * The function must be passed <ul>
14295      * <li>The Record block object</li>
14296      * <li>The "arg" argument from the load function</li>
14297      * <li>A boolean success indicator</li>
14298      * </ul>
14299      * @param {Object} scope The scope in which to call the callback
14300      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14301      */
14302     load : function(params, reader, callback, scope, arg){
14303         params = params || {};
14304         var result;
14305         try {
14306             result = reader.readRecords(params.data ? params.data :this.data);
14307         }catch(e){
14308             this.fireEvent("loadexception", this, arg, null, e);
14309             callback.call(scope, null, arg, false);
14310             return;
14311         }
14312         callback.call(scope, result, arg, true);
14313     },
14314     
14315     // private
14316     update : function(params, records){
14317         
14318     }
14319 });/*
14320  * Based on:
14321  * Ext JS Library 1.1.1
14322  * Copyright(c) 2006-2007, Ext JS, LLC.
14323  *
14324  * Originally Released Under LGPL - original licence link has changed is not relivant.
14325  *
14326  * Fork - LGPL
14327  * <script type="text/javascript">
14328  */
14329 /**
14330  * @class Roo.data.HttpProxy
14331  * @extends Roo.data.DataProxy
14332  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14333  * configured to reference a certain URL.<br><br>
14334  * <p>
14335  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14336  * from which the running page was served.<br><br>
14337  * <p>
14338  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14339  * <p>
14340  * Be aware that to enable the browser to parse an XML document, the server must set
14341  * the Content-Type header in the HTTP response to "text/xml".
14342  * @constructor
14343  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14344  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14345  * will be used to make the request.
14346  */
14347 Roo.data.HttpProxy = function(conn){
14348     Roo.data.HttpProxy.superclass.constructor.call(this);
14349     // is conn a conn config or a real conn?
14350     this.conn = conn;
14351     this.useAjax = !conn || !conn.events;
14352   
14353 };
14354
14355 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14356     // thse are take from connection...
14357     
14358     /**
14359      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14360      */
14361     /**
14362      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14363      * extra parameters to each request made by this object. (defaults to undefined)
14364      */
14365     /**
14366      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14367      *  to each request made by this object. (defaults to undefined)
14368      */
14369     /**
14370      * @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)
14371      */
14372     /**
14373      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14374      */
14375      /**
14376      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14377      * @type Boolean
14378      */
14379   
14380
14381     /**
14382      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14383      * @type Boolean
14384      */
14385     /**
14386      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14387      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14388      * a finer-grained basis than the DataProxy events.
14389      */
14390     getConnection : function(){
14391         return this.useAjax ? Roo.Ajax : this.conn;
14392     },
14393
14394     /**
14395      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14396      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14397      * process that block using the passed callback.
14398      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14399      * for the request to the remote server.
14400      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14401      * object into a block of Roo.data.Records.
14402      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14403      * The function must be passed <ul>
14404      * <li>The Record block object</li>
14405      * <li>The "arg" argument from the load function</li>
14406      * <li>A boolean success indicator</li>
14407      * </ul>
14408      * @param {Object} scope The scope in which to call the callback
14409      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14410      */
14411     load : function(params, reader, callback, scope, arg){
14412         if(this.fireEvent("beforeload", this, params) !== false){
14413             var  o = {
14414                 params : params || {},
14415                 request: {
14416                     callback : callback,
14417                     scope : scope,
14418                     arg : arg
14419                 },
14420                 reader: reader,
14421                 callback : this.loadResponse,
14422                 scope: this
14423             };
14424             if(this.useAjax){
14425                 Roo.applyIf(o, this.conn);
14426                 if(this.activeRequest){
14427                     Roo.Ajax.abort(this.activeRequest);
14428                 }
14429                 this.activeRequest = Roo.Ajax.request(o);
14430             }else{
14431                 this.conn.request(o);
14432             }
14433         }else{
14434             callback.call(scope||this, null, arg, false);
14435         }
14436     },
14437
14438     // private
14439     loadResponse : function(o, success, response){
14440         delete this.activeRequest;
14441         if(!success){
14442             this.fireEvent("loadexception", this, o, response);
14443             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14444             return;
14445         }
14446         var result;
14447         try {
14448             result = o.reader.read(response);
14449         }catch(e){
14450             this.fireEvent("loadexception", this, o, response, e);
14451             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14452             return;
14453         }
14454         
14455         this.fireEvent("load", this, o, o.request.arg);
14456         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14457     },
14458
14459     // private
14460     update : function(dataSet){
14461
14462     },
14463
14464     // private
14465     updateResponse : function(dataSet){
14466
14467     }
14468 });/*
14469  * Based on:
14470  * Ext JS Library 1.1.1
14471  * Copyright(c) 2006-2007, Ext JS, LLC.
14472  *
14473  * Originally Released Under LGPL - original licence link has changed is not relivant.
14474  *
14475  * Fork - LGPL
14476  * <script type="text/javascript">
14477  */
14478
14479 /**
14480  * @class Roo.data.ScriptTagProxy
14481  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14482  * other than the originating domain of the running page.<br><br>
14483  * <p>
14484  * <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
14485  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14486  * <p>
14487  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14488  * source code that is used as the source inside a &lt;script> tag.<br><br>
14489  * <p>
14490  * In order for the browser to process the returned data, the server must wrap the data object
14491  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14492  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14493  * depending on whether the callback name was passed:
14494  * <p>
14495  * <pre><code>
14496 boolean scriptTag = false;
14497 String cb = request.getParameter("callback");
14498 if (cb != null) {
14499     scriptTag = true;
14500     response.setContentType("text/javascript");
14501 } else {
14502     response.setContentType("application/x-json");
14503 }
14504 Writer out = response.getWriter();
14505 if (scriptTag) {
14506     out.write(cb + "(");
14507 }
14508 out.print(dataBlock.toJsonString());
14509 if (scriptTag) {
14510     out.write(");");
14511 }
14512 </pre></code>
14513  *
14514  * @constructor
14515  * @param {Object} config A configuration object.
14516  */
14517 Roo.data.ScriptTagProxy = function(config){
14518     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14519     Roo.apply(this, config);
14520     this.head = document.getElementsByTagName("head")[0];
14521 };
14522
14523 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14524
14525 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14526     /**
14527      * @cfg {String} url The URL from which to request the data object.
14528      */
14529     /**
14530      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14531      */
14532     timeout : 30000,
14533     /**
14534      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14535      * the server the name of the callback function set up by the load call to process the returned data object.
14536      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14537      * javascript output which calls this named function passing the data object as its only parameter.
14538      */
14539     callbackParam : "callback",
14540     /**
14541      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14542      * name to the request.
14543      */
14544     nocache : true,
14545
14546     /**
14547      * Load data from the configured URL, read the data object into
14548      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14549      * process that block using the passed callback.
14550      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14551      * for the request to the remote server.
14552      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14553      * object into a block of Roo.data.Records.
14554      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14555      * The function must be passed <ul>
14556      * <li>The Record block object</li>
14557      * <li>The "arg" argument from the load function</li>
14558      * <li>A boolean success indicator</li>
14559      * </ul>
14560      * @param {Object} scope The scope in which to call the callback
14561      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14562      */
14563     load : function(params, reader, callback, scope, arg){
14564         if(this.fireEvent("beforeload", this, params) !== false){
14565
14566             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14567
14568             var url = this.url;
14569             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14570             if(this.nocache){
14571                 url += "&_dc=" + (new Date().getTime());
14572             }
14573             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14574             var trans = {
14575                 id : transId,
14576                 cb : "stcCallback"+transId,
14577                 scriptId : "stcScript"+transId,
14578                 params : params,
14579                 arg : arg,
14580                 url : url,
14581                 callback : callback,
14582                 scope : scope,
14583                 reader : reader
14584             };
14585             var conn = this;
14586
14587             window[trans.cb] = function(o){
14588                 conn.handleResponse(o, trans);
14589             };
14590
14591             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14592
14593             if(this.autoAbort !== false){
14594                 this.abort();
14595             }
14596
14597             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14598
14599             var script = document.createElement("script");
14600             script.setAttribute("src", url);
14601             script.setAttribute("type", "text/javascript");
14602             script.setAttribute("id", trans.scriptId);
14603             this.head.appendChild(script);
14604
14605             this.trans = trans;
14606         }else{
14607             callback.call(scope||this, null, arg, false);
14608         }
14609     },
14610
14611     // private
14612     isLoading : function(){
14613         return this.trans ? true : false;
14614     },
14615
14616     /**
14617      * Abort the current server request.
14618      */
14619     abort : function(){
14620         if(this.isLoading()){
14621             this.destroyTrans(this.trans);
14622         }
14623     },
14624
14625     // private
14626     destroyTrans : function(trans, isLoaded){
14627         this.head.removeChild(document.getElementById(trans.scriptId));
14628         clearTimeout(trans.timeoutId);
14629         if(isLoaded){
14630             window[trans.cb] = undefined;
14631             try{
14632                 delete window[trans.cb];
14633             }catch(e){}
14634         }else{
14635             // if hasn't been loaded, wait for load to remove it to prevent script error
14636             window[trans.cb] = function(){
14637                 window[trans.cb] = undefined;
14638                 try{
14639                     delete window[trans.cb];
14640                 }catch(e){}
14641             };
14642         }
14643     },
14644
14645     // private
14646     handleResponse : function(o, trans){
14647         this.trans = false;
14648         this.destroyTrans(trans, true);
14649         var result;
14650         try {
14651             result = trans.reader.readRecords(o);
14652         }catch(e){
14653             this.fireEvent("loadexception", this, o, trans.arg, e);
14654             trans.callback.call(trans.scope||window, null, trans.arg, false);
14655             return;
14656         }
14657         this.fireEvent("load", this, o, trans.arg);
14658         trans.callback.call(trans.scope||window, result, trans.arg, true);
14659     },
14660
14661     // private
14662     handleFailure : function(trans){
14663         this.trans = false;
14664         this.destroyTrans(trans, false);
14665         this.fireEvent("loadexception", this, null, trans.arg);
14666         trans.callback.call(trans.scope||window, null, trans.arg, false);
14667     }
14668 });/*
14669  * Based on:
14670  * Ext JS Library 1.1.1
14671  * Copyright(c) 2006-2007, Ext JS, LLC.
14672  *
14673  * Originally Released Under LGPL - original licence link has changed is not relivant.
14674  *
14675  * Fork - LGPL
14676  * <script type="text/javascript">
14677  */
14678
14679 /**
14680  * @class Roo.data.JsonReader
14681  * @extends Roo.data.DataReader
14682  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14683  * based on mappings in a provided Roo.data.Record constructor.
14684  * 
14685  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14686  * in the reply previously. 
14687  * 
14688  * <p>
14689  * Example code:
14690  * <pre><code>
14691 var RecordDef = Roo.data.Record.create([
14692     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14693     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14694 ]);
14695 var myReader = new Roo.data.JsonReader({
14696     totalProperty: "results",    // The property which contains the total dataset size (optional)
14697     root: "rows",                // The property which contains an Array of row objects
14698     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14699 }, RecordDef);
14700 </code></pre>
14701  * <p>
14702  * This would consume a JSON file like this:
14703  * <pre><code>
14704 { 'results': 2, 'rows': [
14705     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14706     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14707 }
14708 </code></pre>
14709  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14710  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14711  * paged from the remote server.
14712  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14713  * @cfg {String} root name of the property which contains the Array of row objects.
14714  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14715  * @cfg {Array} fields Array of field definition objects
14716  * @constructor
14717  * Create a new JsonReader
14718  * @param {Object} meta Metadata configuration options
14719  * @param {Object} recordType Either an Array of field definition objects,
14720  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14721  */
14722 Roo.data.JsonReader = function(meta, recordType){
14723     
14724     meta = meta || {};
14725     // set some defaults:
14726     Roo.applyIf(meta, {
14727         totalProperty: 'total',
14728         successProperty : 'success',
14729         root : 'data',
14730         id : 'id'
14731     });
14732     
14733     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14734 };
14735 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14736     
14737     readerType : 'Json',
14738     
14739     /**
14740      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14741      * Used by Store query builder to append _requestMeta to params.
14742      * 
14743      */
14744     metaFromRemote : false,
14745     /**
14746      * This method is only used by a DataProxy which has retrieved data from a remote server.
14747      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14748      * @return {Object} data A data block which is used by an Roo.data.Store object as
14749      * a cache of Roo.data.Records.
14750      */
14751     read : function(response){
14752         var json = response.responseText;
14753        
14754         var o = /* eval:var:o */ eval("("+json+")");
14755         if(!o) {
14756             throw {message: "JsonReader.read: Json object not found"};
14757         }
14758         
14759         if(o.metaData){
14760             
14761             delete this.ef;
14762             this.metaFromRemote = true;
14763             this.meta = o.metaData;
14764             this.recordType = Roo.data.Record.create(o.metaData.fields);
14765             this.onMetaChange(this.meta, this.recordType, o);
14766         }
14767         return this.readRecords(o);
14768     },
14769
14770     // private function a store will implement
14771     onMetaChange : function(meta, recordType, o){
14772
14773     },
14774
14775     /**
14776          * @ignore
14777          */
14778     simpleAccess: function(obj, subsc) {
14779         return obj[subsc];
14780     },
14781
14782         /**
14783          * @ignore
14784          */
14785     getJsonAccessor: function(){
14786         var re = /[\[\.]/;
14787         return function(expr) {
14788             try {
14789                 return(re.test(expr))
14790                     ? new Function("obj", "return obj." + expr)
14791                     : function(obj){
14792                         return obj[expr];
14793                     };
14794             } catch(e){}
14795             return Roo.emptyFn;
14796         };
14797     }(),
14798
14799     /**
14800      * Create a data block containing Roo.data.Records from an XML document.
14801      * @param {Object} o An object which contains an Array of row objects in the property specified
14802      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14803      * which contains the total size of the dataset.
14804      * @return {Object} data A data block which is used by an Roo.data.Store object as
14805      * a cache of Roo.data.Records.
14806      */
14807     readRecords : function(o){
14808         /**
14809          * After any data loads, the raw JSON data is available for further custom processing.
14810          * @type Object
14811          */
14812         this.o = o;
14813         var s = this.meta, Record = this.recordType,
14814             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14815
14816 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14817         if (!this.ef) {
14818             if(s.totalProperty) {
14819                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14820                 }
14821                 if(s.successProperty) {
14822                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14823                 }
14824                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14825                 if (s.id) {
14826                         var g = this.getJsonAccessor(s.id);
14827                         this.getId = function(rec) {
14828                                 var r = g(rec);  
14829                                 return (r === undefined || r === "") ? null : r;
14830                         };
14831                 } else {
14832                         this.getId = function(){return null;};
14833                 }
14834             this.ef = [];
14835             for(var jj = 0; jj < fl; jj++){
14836                 f = fi[jj];
14837                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14838                 this.ef[jj] = this.getJsonAccessor(map);
14839             }
14840         }
14841
14842         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14843         if(s.totalProperty){
14844             var vt = parseInt(this.getTotal(o), 10);
14845             if(!isNaN(vt)){
14846                 totalRecords = vt;
14847             }
14848         }
14849         if(s.successProperty){
14850             var vs = this.getSuccess(o);
14851             if(vs === false || vs === 'false'){
14852                 success = false;
14853             }
14854         }
14855         var records = [];
14856         for(var i = 0; i < c; i++){
14857                 var n = root[i];
14858             var values = {};
14859             var id = this.getId(n);
14860             for(var j = 0; j < fl; j++){
14861                 f = fi[j];
14862             var v = this.ef[j](n);
14863             if (!f.convert) {
14864                 Roo.log('missing convert for ' + f.name);
14865                 Roo.log(f);
14866                 continue;
14867             }
14868             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14869             }
14870             var record = new Record(values, id);
14871             record.json = n;
14872             records[i] = record;
14873         }
14874         return {
14875             raw : o,
14876             success : success,
14877             records : records,
14878             totalRecords : totalRecords
14879         };
14880     },
14881     // used when loading children.. @see loadDataFromChildren
14882     toLoadData: function(rec)
14883     {
14884         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14885         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14886         return { data : data, total : data.length };
14887         
14888     }
14889 });/*
14890  * Based on:
14891  * Ext JS Library 1.1.1
14892  * Copyright(c) 2006-2007, Ext JS, LLC.
14893  *
14894  * Originally Released Under LGPL - original licence link has changed is not relivant.
14895  *
14896  * Fork - LGPL
14897  * <script type="text/javascript">
14898  */
14899
14900 /**
14901  * @class Roo.data.ArrayReader
14902  * @extends Roo.data.DataReader
14903  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14904  * Each element of that Array represents a row of data fields. The
14905  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14906  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14907  * <p>
14908  * Example code:.
14909  * <pre><code>
14910 var RecordDef = Roo.data.Record.create([
14911     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14912     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14913 ]);
14914 var myReader = new Roo.data.ArrayReader({
14915     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14916 }, RecordDef);
14917 </code></pre>
14918  * <p>
14919  * This would consume an Array like this:
14920  * <pre><code>
14921 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14922   </code></pre>
14923  
14924  * @constructor
14925  * Create a new JsonReader
14926  * @param {Object} meta Metadata configuration options.
14927  * @param {Object|Array} recordType Either an Array of field definition objects
14928  * 
14929  * @cfg {Array} fields Array of field definition objects
14930  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14931  * as specified to {@link Roo.data.Record#create},
14932  * or an {@link Roo.data.Record} object
14933  *
14934  * 
14935  * created using {@link Roo.data.Record#create}.
14936  */
14937 Roo.data.ArrayReader = function(meta, recordType)
14938 {    
14939     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14940 };
14941
14942 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14943     
14944       /**
14945      * Create a data block containing Roo.data.Records from an XML document.
14946      * @param {Object} o An Array of row objects which represents the dataset.
14947      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14948      * a cache of Roo.data.Records.
14949      */
14950     readRecords : function(o)
14951     {
14952         var sid = this.meta ? this.meta.id : null;
14953         var recordType = this.recordType, fields = recordType.prototype.fields;
14954         var records = [];
14955         var root = o;
14956         for(var i = 0; i < root.length; i++){
14957                 var n = root[i];
14958             var values = {};
14959             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14960             for(var j = 0, jlen = fields.length; j < jlen; j++){
14961                 var f = fields.items[j];
14962                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14963                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14964                 v = f.convert(v);
14965                 values[f.name] = v;
14966             }
14967             var record = new recordType(values, id);
14968             record.json = n;
14969             records[records.length] = record;
14970         }
14971         return {
14972             records : records,
14973             totalRecords : records.length
14974         };
14975     },
14976     // used when loading children.. @see loadDataFromChildren
14977     toLoadData: function(rec)
14978     {
14979         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14980         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14981         
14982     }
14983     
14984     
14985 });/*
14986  * - LGPL
14987  * * 
14988  */
14989
14990 /**
14991  * @class Roo.bootstrap.ComboBox
14992  * @extends Roo.bootstrap.TriggerField
14993  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14994  * @cfg {Boolean} append (true|false) default false
14995  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14996  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14997  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14998  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14999  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15000  * @cfg {Boolean} animate default true
15001  * @cfg {Boolean} emptyResultText only for touch device
15002  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15003  * @cfg {String} emptyTitle default ''
15004  * @cfg {Number} width fixed with? experimental
15005  * @constructor
15006  * Create a new ComboBox.
15007  * @param {Object} config Configuration options
15008  */
15009 Roo.bootstrap.ComboBox = function(config){
15010     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15011     this.addEvents({
15012         /**
15013          * @event expand
15014          * Fires when the dropdown list is expanded
15015         * @param {Roo.bootstrap.ComboBox} combo This combo box
15016         */
15017         'expand' : true,
15018         /**
15019          * @event collapse
15020          * Fires when the dropdown list is collapsed
15021         * @param {Roo.bootstrap.ComboBox} combo This combo box
15022         */
15023         'collapse' : true,
15024         /**
15025          * @event beforeselect
15026          * Fires before a list item is selected. Return false to cancel the selection.
15027         * @param {Roo.bootstrap.ComboBox} combo This combo box
15028         * @param {Roo.data.Record} record The data record returned from the underlying store
15029         * @param {Number} index The index of the selected item in the dropdown list
15030         */
15031         'beforeselect' : true,
15032         /**
15033          * @event select
15034          * Fires when a list item is selected
15035         * @param {Roo.bootstrap.ComboBox} combo This combo box
15036         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15037         * @param {Number} index The index of the selected item in the dropdown list
15038         */
15039         'select' : true,
15040         /**
15041          * @event beforequery
15042          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15043          * The event object passed has these properties:
15044         * @param {Roo.bootstrap.ComboBox} combo This combo box
15045         * @param {String} query The query
15046         * @param {Boolean} forceAll true to force "all" query
15047         * @param {Boolean} cancel true to cancel the query
15048         * @param {Object} e The query event object
15049         */
15050         'beforequery': true,
15051          /**
15052          * @event add
15053          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15054         * @param {Roo.bootstrap.ComboBox} combo This combo box
15055         */
15056         'add' : true,
15057         /**
15058          * @event edit
15059          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15060         * @param {Roo.bootstrap.ComboBox} combo This combo box
15061         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15062         */
15063         'edit' : true,
15064         /**
15065          * @event remove
15066          * Fires when the remove value from the combobox array
15067         * @param {Roo.bootstrap.ComboBox} combo This combo box
15068         */
15069         'remove' : true,
15070         /**
15071          * @event afterremove
15072          * Fires when the remove value from the combobox array
15073         * @param {Roo.bootstrap.ComboBox} combo This combo box
15074         */
15075         'afterremove' : true,
15076         /**
15077          * @event specialfilter
15078          * Fires when specialfilter
15079             * @param {Roo.bootstrap.ComboBox} combo This combo box
15080             */
15081         'specialfilter' : true,
15082         /**
15083          * @event tick
15084          * Fires when tick the element
15085             * @param {Roo.bootstrap.ComboBox} combo This combo box
15086             */
15087         'tick' : true,
15088         /**
15089          * @event touchviewdisplay
15090          * Fires when touch view require special display (default is using displayField)
15091             * @param {Roo.bootstrap.ComboBox} combo This combo box
15092             * @param {Object} cfg set html .
15093             */
15094         'touchviewdisplay' : true
15095         
15096     });
15097     
15098     this.item = [];
15099     this.tickItems = [];
15100     
15101     this.selectedIndex = -1;
15102     if(this.mode == 'local'){
15103         if(config.queryDelay === undefined){
15104             this.queryDelay = 10;
15105         }
15106         if(config.minChars === undefined){
15107             this.minChars = 0;
15108         }
15109     }
15110 };
15111
15112 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15113      
15114     /**
15115      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15116      * rendering into an Roo.Editor, defaults to false)
15117      */
15118     /**
15119      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15120      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15121      */
15122     /**
15123      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15124      */
15125     /**
15126      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15127      * the dropdown list (defaults to undefined, with no header element)
15128      */
15129
15130      /**
15131      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15132      */
15133      
15134      /**
15135      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15136      */
15137     listWidth: undefined,
15138     /**
15139      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15140      * mode = 'remote' or 'text' if mode = 'local')
15141      */
15142     displayField: undefined,
15143     
15144     /**
15145      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15146      * mode = 'remote' or 'value' if mode = 'local'). 
15147      * Note: use of a valueField requires the user make a selection
15148      * in order for a value to be mapped.
15149      */
15150     valueField: undefined,
15151     /**
15152      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15153      */
15154     modalTitle : '',
15155     
15156     /**
15157      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15158      * field's data value (defaults to the underlying DOM element's name)
15159      */
15160     hiddenName: undefined,
15161     /**
15162      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15163      */
15164     listClass: '',
15165     /**
15166      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15167      */
15168     selectedClass: 'active',
15169     
15170     /**
15171      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15172      */
15173     shadow:'sides',
15174     /**
15175      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15176      * anchor positions (defaults to 'tl-bl')
15177      */
15178     listAlign: 'tl-bl?',
15179     /**
15180      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15181      */
15182     maxHeight: 300,
15183     /**
15184      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15185      * query specified by the allQuery config option (defaults to 'query')
15186      */
15187     triggerAction: 'query',
15188     /**
15189      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15190      * (defaults to 4, does not apply if editable = false)
15191      */
15192     minChars : 4,
15193     /**
15194      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15195      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15196      */
15197     typeAhead: false,
15198     /**
15199      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15200      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15201      */
15202     queryDelay: 500,
15203     /**
15204      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15205      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15206      */
15207     pageSize: 0,
15208     /**
15209      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15210      * when editable = true (defaults to false)
15211      */
15212     selectOnFocus:false,
15213     /**
15214      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15215      */
15216     queryParam: 'query',
15217     /**
15218      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15219      * when mode = 'remote' (defaults to 'Loading...')
15220      */
15221     loadingText: 'Loading...',
15222     /**
15223      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15224      */
15225     resizable: false,
15226     /**
15227      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15228      */
15229     handleHeight : 8,
15230     /**
15231      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15232      * traditional select (defaults to true)
15233      */
15234     editable: true,
15235     /**
15236      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15237      */
15238     allQuery: '',
15239     /**
15240      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15241      */
15242     mode: 'remote',
15243     /**
15244      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15245      * listWidth has a higher value)
15246      */
15247     minListWidth : 70,
15248     /**
15249      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15250      * allow the user to set arbitrary text into the field (defaults to false)
15251      */
15252     forceSelection:false,
15253     /**
15254      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15255      * if typeAhead = true (defaults to 250)
15256      */
15257     typeAheadDelay : 250,
15258     /**
15259      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15260      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15261      */
15262     valueNotFoundText : undefined,
15263     /**
15264      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15265      */
15266     blockFocus : false,
15267     
15268     /**
15269      * @cfg {Boolean} disableClear Disable showing of clear button.
15270      */
15271     disableClear : false,
15272     /**
15273      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15274      */
15275     alwaysQuery : false,
15276     
15277     /**
15278      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15279      */
15280     multiple : false,
15281     
15282     /**
15283      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15284      */
15285     invalidClass : "has-warning",
15286     
15287     /**
15288      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15289      */
15290     validClass : "has-success",
15291     
15292     /**
15293      * @cfg {Boolean} specialFilter (true|false) special filter default false
15294      */
15295     specialFilter : false,
15296     
15297     /**
15298      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15299      */
15300     mobileTouchView : true,
15301     
15302     /**
15303      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15304      */
15305     useNativeIOS : false,
15306     
15307     /**
15308      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15309      */
15310     mobile_restrict_height : false,
15311     
15312     ios_options : false,
15313     
15314     //private
15315     addicon : false,
15316     editicon: false,
15317     
15318     page: 0,
15319     hasQuery: false,
15320     append: false,
15321     loadNext: false,
15322     autoFocus : true,
15323     tickable : false,
15324     btnPosition : 'right',
15325     triggerList : true,
15326     showToggleBtn : true,
15327     animate : true,
15328     emptyResultText: 'Empty',
15329     triggerText : 'Select',
15330     emptyTitle : '',
15331     width : false,
15332     
15333     // element that contains real text value.. (when hidden is used..)
15334     
15335     getAutoCreate : function()
15336     {   
15337         var cfg = false;
15338         //render
15339         /*
15340          * Render classic select for iso
15341          */
15342         
15343         if(Roo.isIOS && this.useNativeIOS){
15344             cfg = this.getAutoCreateNativeIOS();
15345             return cfg;
15346         }
15347         
15348         /*
15349          * Touch Devices
15350          */
15351         
15352         if(Roo.isTouch && this.mobileTouchView){
15353             cfg = this.getAutoCreateTouchView();
15354             return cfg;;
15355         }
15356         
15357         /*
15358          *  Normal ComboBox
15359          */
15360         if(!this.tickable){
15361             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15362             return cfg;
15363         }
15364         
15365         /*
15366          *  ComboBox with tickable selections
15367          */
15368              
15369         var align = this.labelAlign || this.parentLabelAlign();
15370         
15371         cfg = {
15372             cls : 'form-group roo-combobox-tickable' //input-group
15373         };
15374         
15375         var btn_text_select = '';
15376         var btn_text_done = '';
15377         var btn_text_cancel = '';
15378         
15379         if (this.btn_text_show) {
15380             btn_text_select = 'Select';
15381             btn_text_done = 'Done';
15382             btn_text_cancel = 'Cancel'; 
15383         }
15384         
15385         var buttons = {
15386             tag : 'div',
15387             cls : 'tickable-buttons',
15388             cn : [
15389                 {
15390                     tag : 'button',
15391                     type : 'button',
15392                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15393                     //html : this.triggerText
15394                     html: btn_text_select
15395                 },
15396                 {
15397                     tag : 'button',
15398                     type : 'button',
15399                     name : 'ok',
15400                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15401                     //html : 'Done'
15402                     html: btn_text_done
15403                 },
15404                 {
15405                     tag : 'button',
15406                     type : 'button',
15407                     name : 'cancel',
15408                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15409                     //html : 'Cancel'
15410                     html: btn_text_cancel
15411                 }
15412             ]
15413         };
15414         
15415         if(this.editable){
15416             buttons.cn.unshift({
15417                 tag: 'input',
15418                 cls: 'roo-select2-search-field-input'
15419             });
15420         }
15421         
15422         var _this = this;
15423         
15424         Roo.each(buttons.cn, function(c){
15425             if (_this.size) {
15426                 c.cls += ' btn-' + _this.size;
15427             }
15428
15429             if (_this.disabled) {
15430                 c.disabled = true;
15431             }
15432         });
15433         
15434         var box = {
15435             tag: 'div',
15436             style : 'display: contents',
15437             cn: [
15438                 {
15439                     tag: 'input',
15440                     type : 'hidden',
15441                     cls: 'form-hidden-field'
15442                 },
15443                 {
15444                     tag: 'ul',
15445                     cls: 'roo-select2-choices',
15446                     cn:[
15447                         {
15448                             tag: 'li',
15449                             cls: 'roo-select2-search-field',
15450                             cn: [
15451                                 buttons
15452                             ]
15453                         }
15454                     ]
15455                 }
15456             ]
15457         };
15458         
15459         var combobox = {
15460             cls: 'roo-select2-container input-group roo-select2-container-multi',
15461             cn: [
15462                 
15463                 box
15464 //                {
15465 //                    tag: 'ul',
15466 //                    cls: 'typeahead typeahead-long dropdown-menu',
15467 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15468 //                }
15469             ]
15470         };
15471         
15472         if(this.hasFeedback && !this.allowBlank){
15473             
15474             var feedback = {
15475                 tag: 'span',
15476                 cls: 'glyphicon form-control-feedback'
15477             };
15478
15479             combobox.cn.push(feedback);
15480         }
15481         
15482         
15483         
15484         var indicator = {
15485             tag : 'i',
15486             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15487             tooltip : 'This field is required'
15488         };
15489         if (Roo.bootstrap.version == 4) {
15490             indicator = {
15491                 tag : 'i',
15492                 style : 'display:none'
15493             };
15494         }
15495         if (align ==='left' && this.fieldLabel.length) {
15496             
15497             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15498             
15499             cfg.cn = [
15500                 indicator,
15501                 {
15502                     tag: 'label',
15503                     'for' :  id,
15504                     cls : 'control-label col-form-label',
15505                     html : this.fieldLabel
15506
15507                 },
15508                 {
15509                     cls : "", 
15510                     cn: [
15511                         combobox
15512                     ]
15513                 }
15514
15515             ];
15516             
15517             var labelCfg = cfg.cn[1];
15518             var contentCfg = cfg.cn[2];
15519             
15520
15521             if(this.indicatorpos == 'right'){
15522                 
15523                 cfg.cn = [
15524                     {
15525                         tag: 'label',
15526                         'for' :  id,
15527                         cls : 'control-label col-form-label',
15528                         cn : [
15529                             {
15530                                 tag : 'span',
15531                                 html : this.fieldLabel
15532                             },
15533                             indicator
15534                         ]
15535                     },
15536                     {
15537                         cls : "",
15538                         cn: [
15539                             combobox
15540                         ]
15541                     }
15542
15543                 ];
15544                 
15545                 
15546                 
15547                 labelCfg = cfg.cn[0];
15548                 contentCfg = cfg.cn[1];
15549             
15550             }
15551             
15552             if(this.labelWidth > 12){
15553                 labelCfg.style = "width: " + this.labelWidth + 'px';
15554             }
15555             if(this.width * 1 > 0){
15556                 contentCfg.style = "width: " + this.width + 'px';
15557             }
15558             if(this.labelWidth < 13 && this.labelmd == 0){
15559                 this.labelmd = this.labelWidth;
15560             }
15561             
15562             if(this.labellg > 0){
15563                 labelCfg.cls += ' col-lg-' + this.labellg;
15564                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15565             }
15566             
15567             if(this.labelmd > 0){
15568                 labelCfg.cls += ' col-md-' + this.labelmd;
15569                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15570             }
15571             
15572             if(this.labelsm > 0){
15573                 labelCfg.cls += ' col-sm-' + this.labelsm;
15574                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15575             }
15576             
15577             if(this.labelxs > 0){
15578                 labelCfg.cls += ' col-xs-' + this.labelxs;
15579                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15580             }
15581                 
15582                 
15583         } else if ( this.fieldLabel.length) {
15584 //                Roo.log(" label");
15585                  cfg.cn = [
15586                    indicator,
15587                     {
15588                         tag: 'label',
15589                         //cls : 'input-group-addon',
15590                         html : this.fieldLabel
15591                     },
15592                     combobox
15593                 ];
15594                 
15595                 if(this.indicatorpos == 'right'){
15596                     cfg.cn = [
15597                         {
15598                             tag: 'label',
15599                             //cls : 'input-group-addon',
15600                             html : this.fieldLabel
15601                         },
15602                         indicator,
15603                         combobox
15604                     ];
15605                     
15606                 }
15607
15608         } else {
15609             
15610 //                Roo.log(" no label && no align");
15611                 cfg = combobox
15612                      
15613                 
15614         }
15615          
15616         var settings=this;
15617         ['xs','sm','md','lg'].map(function(size){
15618             if (settings[size]) {
15619                 cfg.cls += ' col-' + size + '-' + settings[size];
15620             }
15621         });
15622         
15623         return cfg;
15624         
15625     },
15626     
15627     _initEventsCalled : false,
15628     
15629     // private
15630     initEvents: function()
15631     {   
15632         if (this._initEventsCalled) { // as we call render... prevent looping...
15633             return;
15634         }
15635         this._initEventsCalled = true;
15636         
15637         if (!this.store) {
15638             throw "can not find store for combo";
15639         }
15640         
15641         this.indicator = this.indicatorEl();
15642         
15643         this.store = Roo.factory(this.store, Roo.data);
15644         this.store.parent = this;
15645         
15646         // if we are building from html. then this element is so complex, that we can not really
15647         // use the rendered HTML.
15648         // so we have to trash and replace the previous code.
15649         if (Roo.XComponent.build_from_html) {
15650             // remove this element....
15651             var e = this.el.dom, k=0;
15652             while (e ) { e = e.previousSibling;  ++k;}
15653
15654             this.el.remove();
15655             
15656             this.el=false;
15657             this.rendered = false;
15658             
15659             this.render(this.parent().getChildContainer(true), k);
15660         }
15661         
15662         if(Roo.isIOS && this.useNativeIOS){
15663             this.initIOSView();
15664             return;
15665         }
15666         
15667         /*
15668          * Touch Devices
15669          */
15670         
15671         if(Roo.isTouch && this.mobileTouchView){
15672             this.initTouchView();
15673             return;
15674         }
15675         
15676         if(this.tickable){
15677             this.initTickableEvents();
15678             return;
15679         }
15680         
15681         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15682         
15683         if(this.hiddenName){
15684             
15685             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15686             
15687             this.hiddenField.dom.value =
15688                 this.hiddenValue !== undefined ? this.hiddenValue :
15689                 this.value !== undefined ? this.value : '';
15690
15691             // prevent input submission
15692             this.el.dom.removeAttribute('name');
15693             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15694              
15695              
15696         }
15697         //if(Roo.isGecko){
15698         //    this.el.dom.setAttribute('autocomplete', 'off');
15699         //}
15700         
15701         var cls = 'x-combo-list';
15702         
15703         //this.list = new Roo.Layer({
15704         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15705         //});
15706         
15707         var _this = this;
15708         
15709         (function(){
15710             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15711             _this.list.setWidth(lw);
15712         }).defer(100);
15713         
15714         this.list.on('mouseover', this.onViewOver, this);
15715         this.list.on('mousemove', this.onViewMove, this);
15716         this.list.on('scroll', this.onViewScroll, this);
15717         
15718         /*
15719         this.list.swallowEvent('mousewheel');
15720         this.assetHeight = 0;
15721
15722         if(this.title){
15723             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15724             this.assetHeight += this.header.getHeight();
15725         }
15726
15727         this.innerList = this.list.createChild({cls:cls+'-inner'});
15728         this.innerList.on('mouseover', this.onViewOver, this);
15729         this.innerList.on('mousemove', this.onViewMove, this);
15730         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15731         
15732         if(this.allowBlank && !this.pageSize && !this.disableClear){
15733             this.footer = this.list.createChild({cls:cls+'-ft'});
15734             this.pageTb = new Roo.Toolbar(this.footer);
15735            
15736         }
15737         if(this.pageSize){
15738             this.footer = this.list.createChild({cls:cls+'-ft'});
15739             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15740                     {pageSize: this.pageSize});
15741             
15742         }
15743         
15744         if (this.pageTb && this.allowBlank && !this.disableClear) {
15745             var _this = this;
15746             this.pageTb.add(new Roo.Toolbar.Fill(), {
15747                 cls: 'x-btn-icon x-btn-clear',
15748                 text: '&#160;',
15749                 handler: function()
15750                 {
15751                     _this.collapse();
15752                     _this.clearValue();
15753                     _this.onSelect(false, -1);
15754                 }
15755             });
15756         }
15757         if (this.footer) {
15758             this.assetHeight += this.footer.getHeight();
15759         }
15760         */
15761             
15762         if(!this.tpl){
15763             this.tpl = Roo.bootstrap.version == 4 ?
15764                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15765                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15766         }
15767
15768         this.view = new Roo.View(this.list, this.tpl, {
15769             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15770         });
15771         //this.view.wrapEl.setDisplayed(false);
15772         this.view.on('click', this.onViewClick, this);
15773         
15774         
15775         this.store.on('beforeload', this.onBeforeLoad, this);
15776         this.store.on('load', this.onLoad, this);
15777         this.store.on('loadexception', this.onLoadException, this);
15778         /*
15779         if(this.resizable){
15780             this.resizer = new Roo.Resizable(this.list,  {
15781                pinned:true, handles:'se'
15782             });
15783             this.resizer.on('resize', function(r, w, h){
15784                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15785                 this.listWidth = w;
15786                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15787                 this.restrictHeight();
15788             }, this);
15789             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15790         }
15791         */
15792         if(!this.editable){
15793             this.editable = true;
15794             this.setEditable(false);
15795         }
15796         
15797         /*
15798         
15799         if (typeof(this.events.add.listeners) != 'undefined') {
15800             
15801             this.addicon = this.wrap.createChild(
15802                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15803        
15804             this.addicon.on('click', function(e) {
15805                 this.fireEvent('add', this);
15806             }, this);
15807         }
15808         if (typeof(this.events.edit.listeners) != 'undefined') {
15809             
15810             this.editicon = this.wrap.createChild(
15811                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15812             if (this.addicon) {
15813                 this.editicon.setStyle('margin-left', '40px');
15814             }
15815             this.editicon.on('click', function(e) {
15816                 
15817                 // we fire even  if inothing is selected..
15818                 this.fireEvent('edit', this, this.lastData );
15819                 
15820             }, this);
15821         }
15822         */
15823         
15824         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15825             "up" : function(e){
15826                 this.inKeyMode = true;
15827                 this.selectPrev();
15828             },
15829
15830             "down" : function(e){
15831                 if(!this.isExpanded()){
15832                     this.onTriggerClick();
15833                 }else{
15834                     this.inKeyMode = true;
15835                     this.selectNext();
15836                 }
15837             },
15838
15839             "enter" : function(e){
15840 //                this.onViewClick();
15841                 //return true;
15842                 this.collapse();
15843                 
15844                 if(this.fireEvent("specialkey", this, e)){
15845                     this.onViewClick(false);
15846                 }
15847                 
15848                 return true;
15849             },
15850
15851             "esc" : function(e){
15852                 this.collapse();
15853             },
15854
15855             "tab" : function(e){
15856                 this.collapse();
15857                 
15858                 if(this.fireEvent("specialkey", this, e)){
15859                     this.onViewClick(false);
15860                 }
15861                 
15862                 return true;
15863             },
15864
15865             scope : this,
15866
15867             doRelay : function(foo, bar, hname){
15868                 if(hname == 'down' || this.scope.isExpanded()){
15869                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15870                 }
15871                 return true;
15872             },
15873
15874             forceKeyDown: true
15875         });
15876         
15877         
15878         this.queryDelay = Math.max(this.queryDelay || 10,
15879                 this.mode == 'local' ? 10 : 250);
15880         
15881         
15882         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15883         
15884         if(this.typeAhead){
15885             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15886         }
15887         if(this.editable !== false){
15888             this.inputEl().on("keyup", this.onKeyUp, this);
15889         }
15890         if(this.forceSelection){
15891             this.inputEl().on('blur', this.doForce, this);
15892         }
15893         
15894         if(this.multiple){
15895             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15896             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15897         }
15898     },
15899     
15900     initTickableEvents: function()
15901     {   
15902         this.createList();
15903         
15904         if(this.hiddenName){
15905             
15906             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15907             
15908             this.hiddenField.dom.value =
15909                 this.hiddenValue !== undefined ? this.hiddenValue :
15910                 this.value !== undefined ? this.value : '';
15911
15912             // prevent input submission
15913             this.el.dom.removeAttribute('name');
15914             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15915              
15916              
15917         }
15918         
15919 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15920         
15921         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15922         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15923         if(this.triggerList){
15924             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15925         }
15926          
15927         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15928         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15929         
15930         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15931         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15932         
15933         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15934         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15935         
15936         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15937         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15938         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15939         
15940         this.okBtn.hide();
15941         this.cancelBtn.hide();
15942         
15943         var _this = this;
15944         
15945         (function(){
15946             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15947             _this.list.setWidth(lw);
15948         }).defer(100);
15949         
15950         this.list.on('mouseover', this.onViewOver, this);
15951         this.list.on('mousemove', this.onViewMove, this);
15952         
15953         this.list.on('scroll', this.onViewScroll, this);
15954         
15955         if(!this.tpl){
15956             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15957                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15958         }
15959
15960         this.view = new Roo.View(this.list, this.tpl, {
15961             singleSelect:true,
15962             tickable:true,
15963             parent:this,
15964             store: this.store,
15965             selectedClass: this.selectedClass
15966         });
15967         
15968         //this.view.wrapEl.setDisplayed(false);
15969         this.view.on('click', this.onViewClick, this);
15970         
15971         
15972         
15973         this.store.on('beforeload', this.onBeforeLoad, this);
15974         this.store.on('load', this.onLoad, this);
15975         this.store.on('loadexception', this.onLoadException, this);
15976         
15977         if(this.editable){
15978             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15979                 "up" : function(e){
15980                     this.inKeyMode = true;
15981                     this.selectPrev();
15982                 },
15983
15984                 "down" : function(e){
15985                     this.inKeyMode = true;
15986                     this.selectNext();
15987                 },
15988
15989                 "enter" : function(e){
15990                     if(this.fireEvent("specialkey", this, e)){
15991                         this.onViewClick(false);
15992                     }
15993                     
15994                     return true;
15995                 },
15996
15997                 "esc" : function(e){
15998                     this.onTickableFooterButtonClick(e, false, false);
15999                 },
16000
16001                 "tab" : function(e){
16002                     this.fireEvent("specialkey", this, e);
16003                     
16004                     this.onTickableFooterButtonClick(e, false, false);
16005                     
16006                     return true;
16007                 },
16008
16009                 scope : this,
16010
16011                 doRelay : function(e, fn, key){
16012                     if(this.scope.isExpanded()){
16013                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16014                     }
16015                     return true;
16016                 },
16017
16018                 forceKeyDown: true
16019             });
16020         }
16021         
16022         this.queryDelay = Math.max(this.queryDelay || 10,
16023                 this.mode == 'local' ? 10 : 250);
16024         
16025         
16026         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16027         
16028         if(this.typeAhead){
16029             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16030         }
16031         
16032         if(this.editable !== false){
16033             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16034         }
16035         
16036         this.indicator = this.indicatorEl();
16037         
16038         if(this.indicator){
16039             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16040             this.indicator.hide();
16041         }
16042         
16043     },
16044
16045     onDestroy : function(){
16046         if(this.view){
16047             this.view.setStore(null);
16048             this.view.el.removeAllListeners();
16049             this.view.el.remove();
16050             this.view.purgeListeners();
16051         }
16052         if(this.list){
16053             this.list.dom.innerHTML  = '';
16054         }
16055         
16056         if(this.store){
16057             this.store.un('beforeload', this.onBeforeLoad, this);
16058             this.store.un('load', this.onLoad, this);
16059             this.store.un('loadexception', this.onLoadException, this);
16060         }
16061         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16062     },
16063
16064     // private
16065     fireKey : function(e){
16066         if(e.isNavKeyPress() && !this.list.isVisible()){
16067             this.fireEvent("specialkey", this, e);
16068         }
16069     },
16070
16071     // private
16072     onResize: function(w, h)
16073     {
16074         
16075         
16076 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16077 //        
16078 //        if(typeof w != 'number'){
16079 //            // we do not handle it!?!?
16080 //            return;
16081 //        }
16082 //        var tw = this.trigger.getWidth();
16083 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16084 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16085 //        var x = w - tw;
16086 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16087 //            
16088 //        //this.trigger.setStyle('left', x+'px');
16089 //        
16090 //        if(this.list && this.listWidth === undefined){
16091 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16092 //            this.list.setWidth(lw);
16093 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16094 //        }
16095         
16096     
16097         
16098     },
16099
16100     /**
16101      * Allow or prevent the user from directly editing the field text.  If false is passed,
16102      * the user will only be able to select from the items defined in the dropdown list.  This method
16103      * is the runtime equivalent of setting the 'editable' config option at config time.
16104      * @param {Boolean} value True to allow the user to directly edit the field text
16105      */
16106     setEditable : function(value){
16107         if(value == this.editable){
16108             return;
16109         }
16110         this.editable = value;
16111         if(!value){
16112             this.inputEl().dom.setAttribute('readOnly', true);
16113             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16114             this.inputEl().addClass('x-combo-noedit');
16115         }else{
16116             this.inputEl().dom.setAttribute('readOnly', false);
16117             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16118             this.inputEl().removeClass('x-combo-noedit');
16119         }
16120     },
16121
16122     // private
16123     
16124     onBeforeLoad : function(combo,opts){
16125         if(!this.hasFocus){
16126             return;
16127         }
16128          if (!opts.add) {
16129             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16130          }
16131         this.restrictHeight();
16132         this.selectedIndex = -1;
16133     },
16134
16135     // private
16136     onLoad : function(){
16137         
16138         this.hasQuery = false;
16139         
16140         if(!this.hasFocus){
16141             return;
16142         }
16143         
16144         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16145             this.loading.hide();
16146         }
16147         
16148         if(this.store.getCount() > 0){
16149             
16150             this.expand();
16151             this.restrictHeight();
16152             if(this.lastQuery == this.allQuery){
16153                 if(this.editable && !this.tickable){
16154                     this.inputEl().dom.select();
16155                 }
16156                 
16157                 if(
16158                     !this.selectByValue(this.value, true) &&
16159                     this.autoFocus && 
16160                     (
16161                         !this.store.lastOptions ||
16162                         typeof(this.store.lastOptions.add) == 'undefined' || 
16163                         this.store.lastOptions.add != true
16164                     )
16165                 ){
16166                     this.select(0, true);
16167                 }
16168             }else{
16169                 if(this.autoFocus){
16170                     this.selectNext();
16171                 }
16172                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16173                     this.taTask.delay(this.typeAheadDelay);
16174                 }
16175             }
16176         }else{
16177             this.onEmptyResults();
16178         }
16179         
16180         //this.el.focus();
16181     },
16182     // private
16183     onLoadException : function()
16184     {
16185         this.hasQuery = false;
16186         
16187         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16188             this.loading.hide();
16189         }
16190         
16191         if(this.tickable && this.editable){
16192             return;
16193         }
16194         
16195         this.collapse();
16196         // only causes errors at present
16197         //Roo.log(this.store.reader.jsonData);
16198         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16199             // fixme
16200             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16201         //}
16202         
16203         
16204     },
16205     // private
16206     onTypeAhead : function(){
16207         if(this.store.getCount() > 0){
16208             var r = this.store.getAt(0);
16209             var newValue = r.data[this.displayField];
16210             var len = newValue.length;
16211             var selStart = this.getRawValue().length;
16212             
16213             if(selStart != len){
16214                 this.setRawValue(newValue);
16215                 this.selectText(selStart, newValue.length);
16216             }
16217         }
16218     },
16219
16220     // private
16221     onSelect : function(record, index){
16222         
16223         if(this.fireEvent('beforeselect', this, record, index) !== false){
16224         
16225             this.setFromData(index > -1 ? record.data : false);
16226             
16227             this.collapse();
16228             this.fireEvent('select', this, record, index);
16229         }
16230     },
16231
16232     /**
16233      * Returns the currently selected field value or empty string if no value is set.
16234      * @return {String} value The selected value
16235      */
16236     getValue : function()
16237     {
16238         if(Roo.isIOS && this.useNativeIOS){
16239             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16240         }
16241         
16242         if(this.multiple){
16243             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16244         }
16245         
16246         if(this.valueField){
16247             return typeof this.value != 'undefined' ? this.value : '';
16248         }else{
16249             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16250         }
16251     },
16252     
16253     getRawValue : function()
16254     {
16255         if(Roo.isIOS && this.useNativeIOS){
16256             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16257         }
16258         
16259         var v = this.inputEl().getValue();
16260         
16261         return v;
16262     },
16263
16264     /**
16265      * Clears any text/value currently set in the field
16266      */
16267     clearValue : function(){
16268         
16269         if(this.hiddenField){
16270             this.hiddenField.dom.value = '';
16271         }
16272         this.value = '';
16273         this.setRawValue('');
16274         this.lastSelectionText = '';
16275         this.lastData = false;
16276         
16277         var close = this.closeTriggerEl();
16278         
16279         if(close){
16280             close.hide();
16281         }
16282         
16283         this.validate();
16284         
16285     },
16286
16287     /**
16288      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16289      * will be displayed in the field.  If the value does not match the data value of an existing item,
16290      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16291      * Otherwise the field will be blank (although the value will still be set).
16292      * @param {String} value The value to match
16293      */
16294     setValue : function(v)
16295     {
16296         if(Roo.isIOS && this.useNativeIOS){
16297             this.setIOSValue(v);
16298             return;
16299         }
16300         
16301         if(this.multiple){
16302             this.syncValue();
16303             return;
16304         }
16305         
16306         var text = v;
16307         if(this.valueField){
16308             var r = this.findRecord(this.valueField, v);
16309             if(r){
16310                 text = r.data[this.displayField];
16311             }else if(this.valueNotFoundText !== undefined){
16312                 text = this.valueNotFoundText;
16313             }
16314         }
16315         this.lastSelectionText = text;
16316         if(this.hiddenField){
16317             this.hiddenField.dom.value = v;
16318         }
16319         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16320         this.value = v;
16321         
16322         var close = this.closeTriggerEl();
16323         
16324         if(close){
16325             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16326         }
16327         
16328         this.validate();
16329     },
16330     /**
16331      * @property {Object} the last set data for the element
16332      */
16333     
16334     lastData : false,
16335     /**
16336      * Sets the value of the field based on a object which is related to the record format for the store.
16337      * @param {Object} value the value to set as. or false on reset?
16338      */
16339     setFromData : function(o){
16340         
16341         if(this.multiple){
16342             this.addItem(o);
16343             return;
16344         }
16345             
16346         var dv = ''; // display value
16347         var vv = ''; // value value..
16348         this.lastData = o;
16349         if (this.displayField) {
16350             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16351         } else {
16352             // this is an error condition!!!
16353             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16354         }
16355         
16356         if(this.valueField){
16357             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16358         }
16359         
16360         var close = this.closeTriggerEl();
16361         
16362         if(close){
16363             if(dv.length || vv * 1 > 0){
16364                 close.show() ;
16365                 this.blockFocus=true;
16366             } else {
16367                 close.hide();
16368             }             
16369         }
16370         
16371         if(this.hiddenField){
16372             this.hiddenField.dom.value = vv;
16373             
16374             this.lastSelectionText = dv;
16375             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16376             this.value = vv;
16377             return;
16378         }
16379         // no hidden field.. - we store the value in 'value', but still display
16380         // display field!!!!
16381         this.lastSelectionText = dv;
16382         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16383         this.value = vv;
16384         
16385         
16386         
16387     },
16388     // private
16389     reset : function(){
16390         // overridden so that last data is reset..
16391         
16392         if(this.multiple){
16393             this.clearItem();
16394             return;
16395         }
16396         
16397         this.setValue(this.originalValue);
16398         //this.clearInvalid();
16399         this.lastData = false;
16400         if (this.view) {
16401             this.view.clearSelections();
16402         }
16403         
16404         this.validate();
16405     },
16406     // private
16407     findRecord : function(prop, value){
16408         var record;
16409         if(this.store.getCount() > 0){
16410             this.store.each(function(r){
16411                 if(r.data[prop] == value){
16412                     record = r;
16413                     return false;
16414                 }
16415                 return true;
16416             });
16417         }
16418         return record;
16419     },
16420     
16421     getName: function()
16422     {
16423         // returns hidden if it's set..
16424         if (!this.rendered) {return ''};
16425         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16426         
16427     },
16428     // private
16429     onViewMove : function(e, t){
16430         this.inKeyMode = false;
16431     },
16432
16433     // private
16434     onViewOver : function(e, t){
16435         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16436             return;
16437         }
16438         var item = this.view.findItemFromChild(t);
16439         
16440         if(item){
16441             var index = this.view.indexOf(item);
16442             this.select(index, false);
16443         }
16444     },
16445
16446     // private
16447     onViewClick : function(view, doFocus, el, e)
16448     {
16449         var index = this.view.getSelectedIndexes()[0];
16450         
16451         var r = this.store.getAt(index);
16452         
16453         if(this.tickable){
16454             
16455             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16456                 return;
16457             }
16458             
16459             var rm = false;
16460             var _this = this;
16461             
16462             Roo.each(this.tickItems, function(v,k){
16463                 
16464                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16465                     Roo.log(v);
16466                     _this.tickItems.splice(k, 1);
16467                     
16468                     if(typeof(e) == 'undefined' && view == false){
16469                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16470                     }
16471                     
16472                     rm = true;
16473                     return;
16474                 }
16475             });
16476             
16477             if(rm){
16478                 return;
16479             }
16480             
16481             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16482                 this.tickItems.push(r.data);
16483             }
16484             
16485             if(typeof(e) == 'undefined' && view == false){
16486                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16487             }
16488                     
16489             return;
16490         }
16491         
16492         if(r){
16493             this.onSelect(r, index);
16494         }
16495         if(doFocus !== false && !this.blockFocus){
16496             this.inputEl().focus();
16497         }
16498     },
16499
16500     // private
16501     restrictHeight : function(){
16502         //this.innerList.dom.style.height = '';
16503         //var inner = this.innerList.dom;
16504         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16505         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16506         //this.list.beginUpdate();
16507         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16508         this.list.alignTo(this.inputEl(), this.listAlign);
16509         this.list.alignTo(this.inputEl(), this.listAlign);
16510         //this.list.endUpdate();
16511     },
16512
16513     // private
16514     onEmptyResults : function(){
16515         
16516         if(this.tickable && this.editable){
16517             this.hasFocus = false;
16518             this.restrictHeight();
16519             return;
16520         }
16521         
16522         this.collapse();
16523     },
16524
16525     /**
16526      * Returns true if the dropdown list is expanded, else false.
16527      */
16528     isExpanded : function(){
16529         return this.list.isVisible();
16530     },
16531
16532     /**
16533      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16534      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16535      * @param {String} value The data value of the item to select
16536      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16537      * selected item if it is not currently in view (defaults to true)
16538      * @return {Boolean} True if the value matched an item in the list, else false
16539      */
16540     selectByValue : function(v, scrollIntoView){
16541         if(v !== undefined && v !== null){
16542             var r = this.findRecord(this.valueField || this.displayField, v);
16543             if(r){
16544                 this.select(this.store.indexOf(r), scrollIntoView);
16545                 return true;
16546             }
16547         }
16548         return false;
16549     },
16550
16551     /**
16552      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16553      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16554      * @param {Number} index The zero-based index of the list item to select
16555      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16556      * selected item if it is not currently in view (defaults to true)
16557      */
16558     select : function(index, scrollIntoView){
16559         this.selectedIndex = index;
16560         this.view.select(index);
16561         if(scrollIntoView !== false){
16562             var el = this.view.getNode(index);
16563             /*
16564              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16565              */
16566             if(el){
16567                 this.list.scrollChildIntoView(el, false);
16568             }
16569         }
16570     },
16571
16572     // private
16573     selectNext : function(){
16574         var ct = this.store.getCount();
16575         if(ct > 0){
16576             if(this.selectedIndex == -1){
16577                 this.select(0);
16578             }else if(this.selectedIndex < ct-1){
16579                 this.select(this.selectedIndex+1);
16580             }
16581         }
16582     },
16583
16584     // private
16585     selectPrev : function(){
16586         var ct = this.store.getCount();
16587         if(ct > 0){
16588             if(this.selectedIndex == -1){
16589                 this.select(0);
16590             }else if(this.selectedIndex != 0){
16591                 this.select(this.selectedIndex-1);
16592             }
16593         }
16594     },
16595
16596     // private
16597     onKeyUp : function(e){
16598         if(this.editable !== false && !e.isSpecialKey()){
16599             this.lastKey = e.getKey();
16600             this.dqTask.delay(this.queryDelay);
16601         }
16602     },
16603
16604     // private
16605     validateBlur : function(){
16606         return !this.list || !this.list.isVisible();   
16607     },
16608
16609     // private
16610     initQuery : function(){
16611         
16612         var v = this.getRawValue();
16613         
16614         if(this.tickable && this.editable){
16615             v = this.tickableInputEl().getValue();
16616         }
16617         
16618         this.doQuery(v);
16619     },
16620
16621     // private
16622     doForce : function(){
16623         if(this.inputEl().dom.value.length > 0){
16624             this.inputEl().dom.value =
16625                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16626              
16627         }
16628     },
16629
16630     /**
16631      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16632      * query allowing the query action to be canceled if needed.
16633      * @param {String} query The SQL query to execute
16634      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16635      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16636      * saved in the current store (defaults to false)
16637      */
16638     doQuery : function(q, forceAll){
16639         
16640         if(q === undefined || q === null){
16641             q = '';
16642         }
16643         var qe = {
16644             query: q,
16645             forceAll: forceAll,
16646             combo: this,
16647             cancel:false
16648         };
16649         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16650             return false;
16651         }
16652         q = qe.query;
16653         
16654         forceAll = qe.forceAll;
16655         if(forceAll === true || (q.length >= this.minChars)){
16656             
16657             this.hasQuery = true;
16658             
16659             if(this.lastQuery != q || this.alwaysQuery){
16660                 this.lastQuery = q;
16661                 if(this.mode == 'local'){
16662                     this.selectedIndex = -1;
16663                     if(forceAll){
16664                         this.store.clearFilter();
16665                     }else{
16666                         
16667                         if(this.specialFilter){
16668                             this.fireEvent('specialfilter', this);
16669                             this.onLoad();
16670                             return;
16671                         }
16672                         
16673                         this.store.filter(this.displayField, q);
16674                     }
16675                     
16676                     this.store.fireEvent("datachanged", this.store);
16677                     
16678                     this.onLoad();
16679                     
16680                     
16681                 }else{
16682                     
16683                     this.store.baseParams[this.queryParam] = q;
16684                     
16685                     var options = {params : this.getParams(q)};
16686                     
16687                     if(this.loadNext){
16688                         options.add = true;
16689                         options.params.start = this.page * this.pageSize;
16690                     }
16691                     
16692                     this.store.load(options);
16693                     
16694                     /*
16695                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16696                      *  we should expand the list on onLoad
16697                      *  so command out it
16698                      */
16699 //                    this.expand();
16700                 }
16701             }else{
16702                 this.selectedIndex = -1;
16703                 this.onLoad();   
16704             }
16705         }
16706         
16707         this.loadNext = false;
16708     },
16709     
16710     // private
16711     getParams : function(q){
16712         var p = {};
16713         //p[this.queryParam] = q;
16714         
16715         if(this.pageSize){
16716             p.start = 0;
16717             p.limit = this.pageSize;
16718         }
16719         return p;
16720     },
16721
16722     /**
16723      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16724      */
16725     collapse : function(){
16726         if(!this.isExpanded()){
16727             return;
16728         }
16729         
16730         this.list.hide();
16731         
16732         this.hasFocus = false;
16733         
16734         if(this.tickable){
16735             this.okBtn.hide();
16736             this.cancelBtn.hide();
16737             this.trigger.show();
16738             
16739             if(this.editable){
16740                 this.tickableInputEl().dom.value = '';
16741                 this.tickableInputEl().blur();
16742             }
16743             
16744         }
16745         
16746         Roo.get(document).un('mousedown', this.collapseIf, this);
16747         Roo.get(document).un('mousewheel', this.collapseIf, this);
16748         if (!this.editable) {
16749             Roo.get(document).un('keydown', this.listKeyPress, this);
16750         }
16751         this.fireEvent('collapse', this);
16752         
16753         this.validate();
16754     },
16755
16756     // private
16757     collapseIf : function(e){
16758         var in_combo  = e.within(this.el);
16759         var in_list =  e.within(this.list);
16760         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16761         
16762         if (in_combo || in_list || is_list) {
16763             //e.stopPropagation();
16764             return;
16765         }
16766         
16767         if(this.tickable){
16768             this.onTickableFooterButtonClick(e, false, false);
16769         }
16770
16771         this.collapse();
16772         
16773     },
16774
16775     /**
16776      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16777      */
16778     expand : function(){
16779        
16780         if(this.isExpanded() || !this.hasFocus){
16781             return;
16782         }
16783         
16784         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16785         this.list.setWidth(lw);
16786         
16787         Roo.log('expand');
16788         
16789         this.list.show();
16790         
16791         this.restrictHeight();
16792         
16793         if(this.tickable){
16794             
16795             this.tickItems = Roo.apply([], this.item);
16796             
16797             this.okBtn.show();
16798             this.cancelBtn.show();
16799             this.trigger.hide();
16800             
16801             if(this.editable){
16802                 this.tickableInputEl().focus();
16803             }
16804             
16805         }
16806         
16807         Roo.get(document).on('mousedown', this.collapseIf, this);
16808         Roo.get(document).on('mousewheel', this.collapseIf, this);
16809         if (!this.editable) {
16810             Roo.get(document).on('keydown', this.listKeyPress, this);
16811         }
16812         
16813         this.fireEvent('expand', this);
16814     },
16815
16816     // private
16817     // Implements the default empty TriggerField.onTriggerClick function
16818     onTriggerClick : function(e)
16819     {
16820         Roo.log('trigger click');
16821         
16822         if(this.disabled || !this.triggerList){
16823             return;
16824         }
16825         
16826         this.page = 0;
16827         this.loadNext = false;
16828         
16829         if(this.isExpanded()){
16830             this.collapse();
16831             if (!this.blockFocus) {
16832                 this.inputEl().focus();
16833             }
16834             
16835         }else {
16836             this.hasFocus = true;
16837             if(this.triggerAction == 'all') {
16838                 this.doQuery(this.allQuery, true);
16839             } else {
16840                 this.doQuery(this.getRawValue());
16841             }
16842             if (!this.blockFocus) {
16843                 this.inputEl().focus();
16844             }
16845         }
16846     },
16847     
16848     onTickableTriggerClick : function(e)
16849     {
16850         if(this.disabled){
16851             return;
16852         }
16853         
16854         this.page = 0;
16855         this.loadNext = false;
16856         this.hasFocus = true;
16857         
16858         if(this.triggerAction == 'all') {
16859             this.doQuery(this.allQuery, true);
16860         } else {
16861             this.doQuery(this.getRawValue());
16862         }
16863     },
16864     
16865     onSearchFieldClick : function(e)
16866     {
16867         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16868             this.onTickableFooterButtonClick(e, false, false);
16869             return;
16870         }
16871         
16872         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16873             return;
16874         }
16875         
16876         this.page = 0;
16877         this.loadNext = false;
16878         this.hasFocus = true;
16879         
16880         if(this.triggerAction == 'all') {
16881             this.doQuery(this.allQuery, true);
16882         } else {
16883             this.doQuery(this.getRawValue());
16884         }
16885     },
16886     
16887     listKeyPress : function(e)
16888     {
16889         //Roo.log('listkeypress');
16890         // scroll to first matching element based on key pres..
16891         if (e.isSpecialKey()) {
16892             return false;
16893         }
16894         var k = String.fromCharCode(e.getKey()).toUpperCase();
16895         //Roo.log(k);
16896         var match  = false;
16897         var csel = this.view.getSelectedNodes();
16898         var cselitem = false;
16899         if (csel.length) {
16900             var ix = this.view.indexOf(csel[0]);
16901             cselitem  = this.store.getAt(ix);
16902             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16903                 cselitem = false;
16904             }
16905             
16906         }
16907         
16908         this.store.each(function(v) { 
16909             if (cselitem) {
16910                 // start at existing selection.
16911                 if (cselitem.id == v.id) {
16912                     cselitem = false;
16913                 }
16914                 return true;
16915             }
16916                 
16917             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16918                 match = this.store.indexOf(v);
16919                 return false;
16920             }
16921             return true;
16922         }, this);
16923         
16924         if (match === false) {
16925             return true; // no more action?
16926         }
16927         // scroll to?
16928         this.view.select(match);
16929         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16930         sn.scrollIntoView(sn.dom.parentNode, false);
16931     },
16932     
16933     onViewScroll : function(e, t){
16934         
16935         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){
16936             return;
16937         }
16938         
16939         this.hasQuery = true;
16940         
16941         this.loading = this.list.select('.loading', true).first();
16942         
16943         if(this.loading === null){
16944             this.list.createChild({
16945                 tag: 'div',
16946                 cls: 'loading roo-select2-more-results roo-select2-active',
16947                 html: 'Loading more results...'
16948             });
16949             
16950             this.loading = this.list.select('.loading', true).first();
16951             
16952             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16953             
16954             this.loading.hide();
16955         }
16956         
16957         this.loading.show();
16958         
16959         var _combo = this;
16960         
16961         this.page++;
16962         this.loadNext = true;
16963         
16964         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16965         
16966         return;
16967     },
16968     
16969     addItem : function(o)
16970     {   
16971         var dv = ''; // display value
16972         
16973         if (this.displayField) {
16974             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16975         } else {
16976             // this is an error condition!!!
16977             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16978         }
16979         
16980         if(!dv.length){
16981             return;
16982         }
16983         
16984         var choice = this.choices.createChild({
16985             tag: 'li',
16986             cls: 'roo-select2-search-choice',
16987             cn: [
16988                 {
16989                     tag: 'div',
16990                     html: dv
16991                 },
16992                 {
16993                     tag: 'a',
16994                     href: '#',
16995                     cls: 'roo-select2-search-choice-close fa fa-times',
16996                     tabindex: '-1'
16997                 }
16998             ]
16999             
17000         }, this.searchField);
17001         
17002         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17003         
17004         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17005         
17006         this.item.push(o);
17007         
17008         this.lastData = o;
17009         
17010         this.syncValue();
17011         
17012         this.inputEl().dom.value = '';
17013         
17014         this.validate();
17015     },
17016     
17017     onRemoveItem : function(e, _self, o)
17018     {
17019         e.preventDefault();
17020         
17021         this.lastItem = Roo.apply([], this.item);
17022         
17023         var index = this.item.indexOf(o.data) * 1;
17024         
17025         if( index < 0){
17026             Roo.log('not this item?!');
17027             return;
17028         }
17029         
17030         this.item.splice(index, 1);
17031         o.item.remove();
17032         
17033         this.syncValue();
17034         
17035         this.fireEvent('remove', this, e);
17036         
17037         this.validate();
17038         
17039     },
17040     
17041     syncValue : function()
17042     {
17043         if(!this.item.length){
17044             this.clearValue();
17045             return;
17046         }
17047             
17048         var value = [];
17049         var _this = this;
17050         Roo.each(this.item, function(i){
17051             if(_this.valueField){
17052                 value.push(i[_this.valueField]);
17053                 return;
17054             }
17055
17056             value.push(i);
17057         });
17058
17059         this.value = value.join(',');
17060
17061         if(this.hiddenField){
17062             this.hiddenField.dom.value = this.value;
17063         }
17064         
17065         this.store.fireEvent("datachanged", this.store);
17066         
17067         this.validate();
17068     },
17069     
17070     clearItem : function()
17071     {
17072         if(!this.multiple){
17073             return;
17074         }
17075         
17076         this.item = [];
17077         
17078         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17079            c.remove();
17080         });
17081         
17082         this.syncValue();
17083         
17084         this.validate();
17085         
17086         if(this.tickable && !Roo.isTouch){
17087             this.view.refresh();
17088         }
17089     },
17090     
17091     inputEl: function ()
17092     {
17093         if(Roo.isIOS && this.useNativeIOS){
17094             return this.el.select('select.roo-ios-select', true).first();
17095         }
17096         
17097         if(Roo.isTouch && this.mobileTouchView){
17098             return this.el.select('input.form-control',true).first();
17099         }
17100         
17101         if(this.tickable){
17102             return this.searchField;
17103         }
17104         
17105         return this.el.select('input.form-control',true).first();
17106     },
17107     
17108     onTickableFooterButtonClick : function(e, btn, el)
17109     {
17110         e.preventDefault();
17111         
17112         this.lastItem = Roo.apply([], this.item);
17113         
17114         if(btn && btn.name == 'cancel'){
17115             this.tickItems = Roo.apply([], this.item);
17116             this.collapse();
17117             return;
17118         }
17119         
17120         this.clearItem();
17121         
17122         var _this = this;
17123         
17124         Roo.each(this.tickItems, function(o){
17125             _this.addItem(o);
17126         });
17127         
17128         this.collapse();
17129         
17130     },
17131     
17132     validate : function()
17133     {
17134         if(this.getVisibilityEl().hasClass('hidden')){
17135             return true;
17136         }
17137         
17138         var v = this.getRawValue();
17139         
17140         if(this.multiple){
17141             v = this.getValue();
17142         }
17143         
17144         if(this.disabled || this.allowBlank || v.length){
17145             this.markValid();
17146             return true;
17147         }
17148         
17149         this.markInvalid();
17150         return false;
17151     },
17152     
17153     tickableInputEl : function()
17154     {
17155         if(!this.tickable || !this.editable){
17156             return this.inputEl();
17157         }
17158         
17159         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17160     },
17161     
17162     
17163     getAutoCreateTouchView : function()
17164     {
17165         var id = Roo.id();
17166         
17167         var cfg = {
17168             cls: 'form-group' //input-group
17169         };
17170         
17171         var input =  {
17172             tag: 'input',
17173             id : id,
17174             type : this.inputType,
17175             cls : 'form-control x-combo-noedit',
17176             autocomplete: 'new-password',
17177             placeholder : this.placeholder || '',
17178             readonly : true
17179         };
17180         
17181         if (this.name) {
17182             input.name = this.name;
17183         }
17184         
17185         if (this.size) {
17186             input.cls += ' input-' + this.size;
17187         }
17188         
17189         if (this.disabled) {
17190             input.disabled = true;
17191         }
17192         
17193         var inputblock = {
17194             cls : 'roo-combobox-wrap',
17195             cn : [
17196                 input
17197             ]
17198         };
17199         
17200         if(this.before){
17201             inputblock.cls += ' input-group';
17202             
17203             inputblock.cn.unshift({
17204                 tag :'span',
17205                 cls : 'input-group-addon input-group-prepend input-group-text',
17206                 html : this.before
17207             });
17208         }
17209         
17210         if(this.removable && !this.multiple){
17211             inputblock.cls += ' roo-removable';
17212             
17213             inputblock.cn.push({
17214                 tag: 'button',
17215                 html : 'x',
17216                 cls : 'roo-combo-removable-btn close'
17217             });
17218         }
17219
17220         if(this.hasFeedback && !this.allowBlank){
17221             
17222             inputblock.cls += ' has-feedback';
17223             
17224             inputblock.cn.push({
17225                 tag: 'span',
17226                 cls: 'glyphicon form-control-feedback'
17227             });
17228             
17229         }
17230         
17231         if (this.after) {
17232             
17233             inputblock.cls += (this.before) ? '' : ' input-group';
17234             
17235             inputblock.cn.push({
17236                 tag :'span',
17237                 cls : 'input-group-addon input-group-append input-group-text',
17238                 html : this.after
17239             });
17240         }
17241
17242         
17243         var ibwrap = inputblock;
17244         
17245         if(this.multiple){
17246             ibwrap = {
17247                 tag: 'ul',
17248                 cls: 'roo-select2-choices',
17249                 cn:[
17250                     {
17251                         tag: 'li',
17252                         cls: 'roo-select2-search-field',
17253                         cn: [
17254
17255                             inputblock
17256                         ]
17257                     }
17258                 ]
17259             };
17260         
17261             
17262         }
17263         
17264         var combobox = {
17265             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17266             cn: [
17267                 {
17268                     tag: 'input',
17269                     type : 'hidden',
17270                     cls: 'form-hidden-field'
17271                 },
17272                 ibwrap
17273             ]
17274         };
17275         
17276         if(!this.multiple && this.showToggleBtn){
17277             
17278             var caret = {
17279                 cls: 'caret'
17280             };
17281             
17282             if (this.caret != false) {
17283                 caret = {
17284                      tag: 'i',
17285                      cls: 'fa fa-' + this.caret
17286                 };
17287                 
17288             }
17289             
17290             combobox.cn.push({
17291                 tag :'span',
17292                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17293                 cn : [
17294                     Roo.bootstrap.version == 3 ? caret : '',
17295                     {
17296                         tag: 'span',
17297                         cls: 'combobox-clear',
17298                         cn  : [
17299                             {
17300                                 tag : 'i',
17301                                 cls: 'icon-remove'
17302                             }
17303                         ]
17304                     }
17305                 ]
17306
17307             })
17308         }
17309         
17310         if(this.multiple){
17311             combobox.cls += ' roo-select2-container-multi';
17312         }
17313         
17314         var align = this.labelAlign || this.parentLabelAlign();
17315         
17316         if (align ==='left' && this.fieldLabel.length) {
17317
17318             cfg.cn = [
17319                 {
17320                    tag : 'i',
17321                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17322                    tooltip : 'This field is required'
17323                 },
17324                 {
17325                     tag: 'label',
17326                     cls : 'control-label col-form-label',
17327                     html : this.fieldLabel
17328
17329                 },
17330                 {
17331                     cls : 'roo-combobox-wrap ', 
17332                     cn: [
17333                         combobox
17334                     ]
17335                 }
17336             ];
17337             
17338             var labelCfg = cfg.cn[1];
17339             var contentCfg = cfg.cn[2];
17340             
17341
17342             if(this.indicatorpos == 'right'){
17343                 cfg.cn = [
17344                     {
17345                         tag: 'label',
17346                         'for' :  id,
17347                         cls : 'control-label col-form-label',
17348                         cn : [
17349                             {
17350                                 tag : 'span',
17351                                 html : this.fieldLabel
17352                             },
17353                             {
17354                                 tag : 'i',
17355                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17356                                 tooltip : 'This field is required'
17357                             }
17358                         ]
17359                     },
17360                     {
17361                         cls : "roo-combobox-wrap ",
17362                         cn: [
17363                             combobox
17364                         ]
17365                     }
17366
17367                 ];
17368                 
17369                 labelCfg = cfg.cn[0];
17370                 contentCfg = cfg.cn[1];
17371             }
17372             
17373            
17374             
17375             if(this.labelWidth > 12){
17376                 labelCfg.style = "width: " + this.labelWidth + 'px';
17377             }
17378            
17379             if(this.labelWidth < 13 && this.labelmd == 0){
17380                 this.labelmd = this.labelWidth;
17381             }
17382             
17383             if(this.labellg > 0){
17384                 labelCfg.cls += ' col-lg-' + this.labellg;
17385                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17386             }
17387             
17388             if(this.labelmd > 0){
17389                 labelCfg.cls += ' col-md-' + this.labelmd;
17390                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17391             }
17392             
17393             if(this.labelsm > 0){
17394                 labelCfg.cls += ' col-sm-' + this.labelsm;
17395                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17396             }
17397             
17398             if(this.labelxs > 0){
17399                 labelCfg.cls += ' col-xs-' + this.labelxs;
17400                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17401             }
17402                 
17403                 
17404         } else if ( this.fieldLabel.length) {
17405             cfg.cn = [
17406                 {
17407                    tag : 'i',
17408                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17409                    tooltip : 'This field is required'
17410                 },
17411                 {
17412                     tag: 'label',
17413                     cls : 'control-label',
17414                     html : this.fieldLabel
17415
17416                 },
17417                 {
17418                     cls : '', 
17419                     cn: [
17420                         combobox
17421                     ]
17422                 }
17423             ];
17424             
17425             if(this.indicatorpos == 'right'){
17426                 cfg.cn = [
17427                     {
17428                         tag: 'label',
17429                         cls : 'control-label',
17430                         html : this.fieldLabel,
17431                         cn : [
17432                             {
17433                                tag : 'i',
17434                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17435                                tooltip : 'This field is required'
17436                             }
17437                         ]
17438                     },
17439                     {
17440                         cls : '', 
17441                         cn: [
17442                             combobox
17443                         ]
17444                     }
17445                 ];
17446             }
17447         } else {
17448             cfg.cn = combobox;    
17449         }
17450         
17451         
17452         var settings = this;
17453         
17454         ['xs','sm','md','lg'].map(function(size){
17455             if (settings[size]) {
17456                 cfg.cls += ' col-' + size + '-' + settings[size];
17457             }
17458         });
17459         
17460         return cfg;
17461     },
17462     
17463     initTouchView : function()
17464     {
17465         this.renderTouchView();
17466         
17467         this.touchViewEl.on('scroll', function(){
17468             this.el.dom.scrollTop = 0;
17469         }, this);
17470         
17471         this.originalValue = this.getValue();
17472         
17473         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17474         
17475         this.inputEl().on("click", this.showTouchView, this);
17476         if (this.triggerEl) {
17477             this.triggerEl.on("click", this.showTouchView, this);
17478         }
17479         
17480         
17481         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17482         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17483         
17484         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17485         
17486         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17487         this.store.on('load', this.onTouchViewLoad, this);
17488         this.store.on('loadexception', this.onTouchViewLoadException, this);
17489         
17490         if(this.hiddenName){
17491             
17492             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17493             
17494             this.hiddenField.dom.value =
17495                 this.hiddenValue !== undefined ? this.hiddenValue :
17496                 this.value !== undefined ? this.value : '';
17497         
17498             this.el.dom.removeAttribute('name');
17499             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17500         }
17501         
17502         if(this.multiple){
17503             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17504             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17505         }
17506         
17507         if(this.removable && !this.multiple){
17508             var close = this.closeTriggerEl();
17509             if(close){
17510                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17511                 close.on('click', this.removeBtnClick, this, close);
17512             }
17513         }
17514         /*
17515          * fix the bug in Safari iOS8
17516          */
17517         this.inputEl().on("focus", function(e){
17518             document.activeElement.blur();
17519         }, this);
17520         
17521         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17522         
17523         return;
17524         
17525         
17526     },
17527     
17528     renderTouchView : function()
17529     {
17530         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17531         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17532         
17533         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17534         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17535         
17536         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17537         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17538         this.touchViewBodyEl.setStyle('overflow', 'auto');
17539         
17540         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17541         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17542         
17543         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17544         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17545         
17546     },
17547     
17548     showTouchView : function()
17549     {
17550         if(this.disabled){
17551             return;
17552         }
17553         
17554         this.touchViewHeaderEl.hide();
17555
17556         if(this.modalTitle.length){
17557             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17558             this.touchViewHeaderEl.show();
17559         }
17560
17561         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17562         this.touchViewEl.show();
17563
17564         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17565         
17566         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17567         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17568
17569         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17570
17571         if(this.modalTitle.length){
17572             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17573         }
17574         
17575         this.touchViewBodyEl.setHeight(bodyHeight);
17576
17577         if(this.animate){
17578             var _this = this;
17579             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17580         }else{
17581             this.touchViewEl.addClass(['in','show']);
17582         }
17583         
17584         if(this._touchViewMask){
17585             Roo.get(document.body).addClass("x-body-masked");
17586             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17587             this._touchViewMask.setStyle('z-index', 10000);
17588             this._touchViewMask.addClass('show');
17589         }
17590         
17591         this.doTouchViewQuery();
17592         
17593     },
17594     
17595     hideTouchView : function()
17596     {
17597         this.touchViewEl.removeClass(['in','show']);
17598
17599         if(this.animate){
17600             var _this = this;
17601             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17602         }else{
17603             this.touchViewEl.setStyle('display', 'none');
17604         }
17605         
17606         if(this._touchViewMask){
17607             this._touchViewMask.removeClass('show');
17608             Roo.get(document.body).removeClass("x-body-masked");
17609         }
17610     },
17611     
17612     setTouchViewValue : function()
17613     {
17614         if(this.multiple){
17615             this.clearItem();
17616         
17617             var _this = this;
17618
17619             Roo.each(this.tickItems, function(o){
17620                 this.addItem(o);
17621             }, this);
17622         }
17623         
17624         this.hideTouchView();
17625     },
17626     
17627     doTouchViewQuery : function()
17628     {
17629         var qe = {
17630             query: '',
17631             forceAll: true,
17632             combo: this,
17633             cancel:false
17634         };
17635         
17636         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17637             return false;
17638         }
17639         
17640         if(!this.alwaysQuery || this.mode == 'local'){
17641             this.onTouchViewLoad();
17642             return;
17643         }
17644         
17645         this.store.load();
17646     },
17647     
17648     onTouchViewBeforeLoad : function(combo,opts)
17649     {
17650         return;
17651     },
17652
17653     // private
17654     onTouchViewLoad : function()
17655     {
17656         if(this.store.getCount() < 1){
17657             this.onTouchViewEmptyResults();
17658             return;
17659         }
17660         
17661         this.clearTouchView();
17662         
17663         var rawValue = this.getRawValue();
17664         
17665         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17666         
17667         this.tickItems = [];
17668         
17669         this.store.data.each(function(d, rowIndex){
17670             var row = this.touchViewListGroup.createChild(template);
17671             
17672             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17673                 row.addClass(d.data.cls);
17674             }
17675             
17676             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17677                 var cfg = {
17678                     data : d.data,
17679                     html : d.data[this.displayField]
17680                 };
17681                 
17682                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17683                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17684                 }
17685             }
17686             row.removeClass('selected');
17687             if(!this.multiple && this.valueField &&
17688                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17689             {
17690                 // radio buttons..
17691                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17692                 row.addClass('selected');
17693             }
17694             
17695             if(this.multiple && this.valueField &&
17696                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17697             {
17698                 
17699                 // checkboxes...
17700                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17701                 this.tickItems.push(d.data);
17702             }
17703             
17704             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17705             
17706         }, this);
17707         
17708         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17709         
17710         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17711
17712         if(this.modalTitle.length){
17713             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17714         }
17715
17716         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17717         
17718         if(this.mobile_restrict_height && listHeight < bodyHeight){
17719             this.touchViewBodyEl.setHeight(listHeight);
17720         }
17721         
17722         var _this = this;
17723         
17724         if(firstChecked && listHeight > bodyHeight){
17725             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17726         }
17727         
17728     },
17729     
17730     onTouchViewLoadException : function()
17731     {
17732         this.hideTouchView();
17733     },
17734     
17735     onTouchViewEmptyResults : function()
17736     {
17737         this.clearTouchView();
17738         
17739         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17740         
17741         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17742         
17743     },
17744     
17745     clearTouchView : function()
17746     {
17747         this.touchViewListGroup.dom.innerHTML = '';
17748     },
17749     
17750     onTouchViewClick : function(e, el, o)
17751     {
17752         e.preventDefault();
17753         
17754         var row = o.row;
17755         var rowIndex = o.rowIndex;
17756         
17757         var r = this.store.getAt(rowIndex);
17758         
17759         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17760             
17761             if(!this.multiple){
17762                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17763                     c.dom.removeAttribute('checked');
17764                 }, this);
17765
17766                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17767
17768                 this.setFromData(r.data);
17769
17770                 var close = this.closeTriggerEl();
17771
17772                 if(close){
17773                     close.show();
17774                 }
17775
17776                 this.hideTouchView();
17777
17778                 this.fireEvent('select', this, r, rowIndex);
17779
17780                 return;
17781             }
17782
17783             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17784                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17785                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17786                 return;
17787             }
17788
17789             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17790             this.addItem(r.data);
17791             this.tickItems.push(r.data);
17792         }
17793     },
17794     
17795     getAutoCreateNativeIOS : function()
17796     {
17797         var cfg = {
17798             cls: 'form-group' //input-group,
17799         };
17800         
17801         var combobox =  {
17802             tag: 'select',
17803             cls : 'roo-ios-select'
17804         };
17805         
17806         if (this.name) {
17807             combobox.name = this.name;
17808         }
17809         
17810         if (this.disabled) {
17811             combobox.disabled = true;
17812         }
17813         
17814         var settings = this;
17815         
17816         ['xs','sm','md','lg'].map(function(size){
17817             if (settings[size]) {
17818                 cfg.cls += ' col-' + size + '-' + settings[size];
17819             }
17820         });
17821         
17822         cfg.cn = combobox;
17823         
17824         return cfg;
17825         
17826     },
17827     
17828     initIOSView : function()
17829     {
17830         this.store.on('load', this.onIOSViewLoad, this);
17831         
17832         return;
17833     },
17834     
17835     onIOSViewLoad : function()
17836     {
17837         if(this.store.getCount() < 1){
17838             return;
17839         }
17840         
17841         this.clearIOSView();
17842         
17843         if(this.allowBlank) {
17844             
17845             var default_text = '-- SELECT --';
17846             
17847             if(this.placeholder.length){
17848                 default_text = this.placeholder;
17849             }
17850             
17851             if(this.emptyTitle.length){
17852                 default_text += ' - ' + this.emptyTitle + ' -';
17853             }
17854             
17855             var opt = this.inputEl().createChild({
17856                 tag: 'option',
17857                 value : 0,
17858                 html : default_text
17859             });
17860             
17861             var o = {};
17862             o[this.valueField] = 0;
17863             o[this.displayField] = default_text;
17864             
17865             this.ios_options.push({
17866                 data : o,
17867                 el : opt
17868             });
17869             
17870         }
17871         
17872         this.store.data.each(function(d, rowIndex){
17873             
17874             var html = '';
17875             
17876             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17877                 html = d.data[this.displayField];
17878             }
17879             
17880             var value = '';
17881             
17882             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17883                 value = d.data[this.valueField];
17884             }
17885             
17886             var option = {
17887                 tag: 'option',
17888                 value : value,
17889                 html : html
17890             };
17891             
17892             if(this.value == d.data[this.valueField]){
17893                 option['selected'] = true;
17894             }
17895             
17896             var opt = this.inputEl().createChild(option);
17897             
17898             this.ios_options.push({
17899                 data : d.data,
17900                 el : opt
17901             });
17902             
17903         }, this);
17904         
17905         this.inputEl().on('change', function(){
17906            this.fireEvent('select', this);
17907         }, this);
17908         
17909     },
17910     
17911     clearIOSView: function()
17912     {
17913         this.inputEl().dom.innerHTML = '';
17914         
17915         this.ios_options = [];
17916     },
17917     
17918     setIOSValue: function(v)
17919     {
17920         this.value = v;
17921         
17922         if(!this.ios_options){
17923             return;
17924         }
17925         
17926         Roo.each(this.ios_options, function(opts){
17927            
17928            opts.el.dom.removeAttribute('selected');
17929            
17930            if(opts.data[this.valueField] != v){
17931                return;
17932            }
17933            
17934            opts.el.dom.setAttribute('selected', true);
17935            
17936         }, this);
17937     }
17938
17939     /** 
17940     * @cfg {Boolean} grow 
17941     * @hide 
17942     */
17943     /** 
17944     * @cfg {Number} growMin 
17945     * @hide 
17946     */
17947     /** 
17948     * @cfg {Number} growMax 
17949     * @hide 
17950     */
17951     /**
17952      * @hide
17953      * @method autoSize
17954      */
17955 });
17956
17957 Roo.apply(Roo.bootstrap.ComboBox,  {
17958     
17959     header : {
17960         tag: 'div',
17961         cls: 'modal-header',
17962         cn: [
17963             {
17964                 tag: 'h4',
17965                 cls: 'modal-title'
17966             }
17967         ]
17968     },
17969     
17970     body : {
17971         tag: 'div',
17972         cls: 'modal-body',
17973         cn: [
17974             {
17975                 tag: 'ul',
17976                 cls: 'list-group'
17977             }
17978         ]
17979     },
17980     
17981     listItemRadio : {
17982         tag: 'li',
17983         cls: 'list-group-item',
17984         cn: [
17985             {
17986                 tag: 'span',
17987                 cls: 'roo-combobox-list-group-item-value'
17988             },
17989             {
17990                 tag: 'div',
17991                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17992                 cn: [
17993                     {
17994                         tag: 'input',
17995                         type: 'radio'
17996                     },
17997                     {
17998                         tag: 'label'
17999                     }
18000                 ]
18001             }
18002         ]
18003     },
18004     
18005     listItemCheckbox : {
18006         tag: 'li',
18007         cls: 'list-group-item',
18008         cn: [
18009             {
18010                 tag: 'span',
18011                 cls: 'roo-combobox-list-group-item-value'
18012             },
18013             {
18014                 tag: 'div',
18015                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18016                 cn: [
18017                     {
18018                         tag: 'input',
18019                         type: 'checkbox'
18020                     },
18021                     {
18022                         tag: 'label'
18023                     }
18024                 ]
18025             }
18026         ]
18027     },
18028     
18029     emptyResult : {
18030         tag: 'div',
18031         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18032     },
18033     
18034     footer : {
18035         tag: 'div',
18036         cls: 'modal-footer',
18037         cn: [
18038             {
18039                 tag: 'div',
18040                 cls: 'row',
18041                 cn: [
18042                     {
18043                         tag: 'div',
18044                         cls: 'col-xs-6 text-left',
18045                         cn: {
18046                             tag: 'button',
18047                             cls: 'btn btn-danger roo-touch-view-cancel',
18048                             html: 'Cancel'
18049                         }
18050                     },
18051                     {
18052                         tag: 'div',
18053                         cls: 'col-xs-6 text-right',
18054                         cn: {
18055                             tag: 'button',
18056                             cls: 'btn btn-success roo-touch-view-ok',
18057                             html: 'OK'
18058                         }
18059                     }
18060                 ]
18061             }
18062         ]
18063         
18064     }
18065 });
18066
18067 Roo.apply(Roo.bootstrap.ComboBox,  {
18068     
18069     touchViewTemplate : {
18070         tag: 'div',
18071         cls: 'modal fade roo-combobox-touch-view',
18072         cn: [
18073             {
18074                 tag: 'div',
18075                 cls: 'modal-dialog',
18076                 style : 'position:fixed', // we have to fix position....
18077                 cn: [
18078                     {
18079                         tag: 'div',
18080                         cls: 'modal-content',
18081                         cn: [
18082                             Roo.bootstrap.ComboBox.header,
18083                             Roo.bootstrap.ComboBox.body,
18084                             Roo.bootstrap.ComboBox.footer
18085                         ]
18086                     }
18087                 ]
18088             }
18089         ]
18090     }
18091 });/*
18092  * Based on:
18093  * Ext JS Library 1.1.1
18094  * Copyright(c) 2006-2007, Ext JS, LLC.
18095  *
18096  * Originally Released Under LGPL - original licence link has changed is not relivant.
18097  *
18098  * Fork - LGPL
18099  * <script type="text/javascript">
18100  */
18101
18102 /**
18103  * @class Roo.View
18104  * @extends Roo.util.Observable
18105  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18106  * This class also supports single and multi selection modes. <br>
18107  * Create a data model bound view:
18108  <pre><code>
18109  var store = new Roo.data.Store(...);
18110
18111  var view = new Roo.View({
18112     el : "my-element",
18113     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18114  
18115     singleSelect: true,
18116     selectedClass: "ydataview-selected",
18117     store: store
18118  });
18119
18120  // listen for node click?
18121  view.on("click", function(vw, index, node, e){
18122  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18123  });
18124
18125  // load XML data
18126  dataModel.load("foobar.xml");
18127  </code></pre>
18128  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18129  * <br><br>
18130  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18131  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18132  * 
18133  * Note: old style constructor is still suported (container, template, config)
18134  * 
18135  * @constructor
18136  * Create a new View
18137  * @param {Object} config The config object
18138  * 
18139  */
18140 Roo.View = function(config, depreciated_tpl, depreciated_config){
18141     
18142     this.parent = false;
18143     
18144     if (typeof(depreciated_tpl) == 'undefined') {
18145         // new way.. - universal constructor.
18146         Roo.apply(this, config);
18147         this.el  = Roo.get(this.el);
18148     } else {
18149         // old format..
18150         this.el  = Roo.get(config);
18151         this.tpl = depreciated_tpl;
18152         Roo.apply(this, depreciated_config);
18153     }
18154     this.wrapEl  = this.el.wrap().wrap();
18155     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18156     
18157     
18158     if(typeof(this.tpl) == "string"){
18159         this.tpl = new Roo.Template(this.tpl);
18160     } else {
18161         // support xtype ctors..
18162         this.tpl = new Roo.factory(this.tpl, Roo);
18163     }
18164     
18165     
18166     this.tpl.compile();
18167     
18168     /** @private */
18169     this.addEvents({
18170         /**
18171          * @event beforeclick
18172          * Fires before a click is processed. Returns false to cancel the default action.
18173          * @param {Roo.View} this
18174          * @param {Number} index The index of the target node
18175          * @param {HTMLElement} node The target node
18176          * @param {Roo.EventObject} e The raw event object
18177          */
18178             "beforeclick" : true,
18179         /**
18180          * @event click
18181          * Fires when a template node is clicked.
18182          * @param {Roo.View} this
18183          * @param {Number} index The index of the target node
18184          * @param {HTMLElement} node The target node
18185          * @param {Roo.EventObject} e The raw event object
18186          */
18187             "click" : true,
18188         /**
18189          * @event dblclick
18190          * Fires when a template node is double clicked.
18191          * @param {Roo.View} this
18192          * @param {Number} index The index of the target node
18193          * @param {HTMLElement} node The target node
18194          * @param {Roo.EventObject} e The raw event object
18195          */
18196             "dblclick" : true,
18197         /**
18198          * @event contextmenu
18199          * Fires when a template node is right clicked.
18200          * @param {Roo.View} this
18201          * @param {Number} index The index of the target node
18202          * @param {HTMLElement} node The target node
18203          * @param {Roo.EventObject} e The raw event object
18204          */
18205             "contextmenu" : true,
18206         /**
18207          * @event selectionchange
18208          * Fires when the selected nodes change.
18209          * @param {Roo.View} this
18210          * @param {Array} selections Array of the selected nodes
18211          */
18212             "selectionchange" : true,
18213     
18214         /**
18215          * @event beforeselect
18216          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18217          * @param {Roo.View} this
18218          * @param {HTMLElement} node The node to be selected
18219          * @param {Array} selections Array of currently selected nodes
18220          */
18221             "beforeselect" : true,
18222         /**
18223          * @event preparedata
18224          * Fires on every row to render, to allow you to change the data.
18225          * @param {Roo.View} this
18226          * @param {Object} data to be rendered (change this)
18227          */
18228           "preparedata" : true
18229           
18230           
18231         });
18232
18233
18234
18235     this.el.on({
18236         "click": this.onClick,
18237         "dblclick": this.onDblClick,
18238         "contextmenu": this.onContextMenu,
18239         scope:this
18240     });
18241
18242     this.selections = [];
18243     this.nodes = [];
18244     this.cmp = new Roo.CompositeElementLite([]);
18245     if(this.store){
18246         this.store = Roo.factory(this.store, Roo.data);
18247         this.setStore(this.store, true);
18248     }
18249     
18250     if ( this.footer && this.footer.xtype) {
18251            
18252          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18253         
18254         this.footer.dataSource = this.store;
18255         this.footer.container = fctr;
18256         this.footer = Roo.factory(this.footer, Roo);
18257         fctr.insertFirst(this.el);
18258         
18259         // this is a bit insane - as the paging toolbar seems to detach the el..
18260 //        dom.parentNode.parentNode.parentNode
18261          // they get detached?
18262     }
18263     
18264     
18265     Roo.View.superclass.constructor.call(this);
18266     
18267     
18268 };
18269
18270 Roo.extend(Roo.View, Roo.util.Observable, {
18271     
18272      /**
18273      * @cfg {Roo.data.Store} store Data store to load data from.
18274      */
18275     store : false,
18276     
18277     /**
18278      * @cfg {String|Roo.Element} el The container element.
18279      */
18280     el : '',
18281     
18282     /**
18283      * @cfg {String|Roo.Template} tpl The template used by this View 
18284      */
18285     tpl : false,
18286     /**
18287      * @cfg {String} dataName the named area of the template to use as the data area
18288      *                          Works with domtemplates roo-name="name"
18289      */
18290     dataName: false,
18291     /**
18292      * @cfg {String} selectedClass The css class to add to selected nodes
18293      */
18294     selectedClass : "x-view-selected",
18295      /**
18296      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18297      */
18298     emptyText : "",
18299     
18300     /**
18301      * @cfg {String} text to display on mask (default Loading)
18302      */
18303     mask : false,
18304     /**
18305      * @cfg {Boolean} multiSelect Allow multiple selection
18306      */
18307     multiSelect : false,
18308     /**
18309      * @cfg {Boolean} singleSelect Allow single selection
18310      */
18311     singleSelect:  false,
18312     
18313     /**
18314      * @cfg {Boolean} toggleSelect - selecting 
18315      */
18316     toggleSelect : false,
18317     
18318     /**
18319      * @cfg {Boolean} tickable - selecting 
18320      */
18321     tickable : false,
18322     
18323     /**
18324      * Returns the element this view is bound to.
18325      * @return {Roo.Element}
18326      */
18327     getEl : function(){
18328         return this.wrapEl;
18329     },
18330     
18331     
18332
18333     /**
18334      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18335      */
18336     refresh : function(){
18337         //Roo.log('refresh');
18338         var t = this.tpl;
18339         
18340         // if we are using something like 'domtemplate', then
18341         // the what gets used is:
18342         // t.applySubtemplate(NAME, data, wrapping data..)
18343         // the outer template then get' applied with
18344         //     the store 'extra data'
18345         // and the body get's added to the
18346         //      roo-name="data" node?
18347         //      <span class='roo-tpl-{name}'></span> ?????
18348         
18349         
18350         
18351         this.clearSelections();
18352         this.el.update("");
18353         var html = [];
18354         var records = this.store.getRange();
18355         if(records.length < 1) {
18356             
18357             // is this valid??  = should it render a template??
18358             
18359             this.el.update(this.emptyText);
18360             return;
18361         }
18362         var el = this.el;
18363         if (this.dataName) {
18364             this.el.update(t.apply(this.store.meta)); //????
18365             el = this.el.child('.roo-tpl-' + this.dataName);
18366         }
18367         
18368         for(var i = 0, len = records.length; i < len; i++){
18369             var data = this.prepareData(records[i].data, i, records[i]);
18370             this.fireEvent("preparedata", this, data, i, records[i]);
18371             
18372             var d = Roo.apply({}, data);
18373             
18374             if(this.tickable){
18375                 Roo.apply(d, {'roo-id' : Roo.id()});
18376                 
18377                 var _this = this;
18378             
18379                 Roo.each(this.parent.item, function(item){
18380                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18381                         return;
18382                     }
18383                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18384                 });
18385             }
18386             
18387             html[html.length] = Roo.util.Format.trim(
18388                 this.dataName ?
18389                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18390                     t.apply(d)
18391             );
18392         }
18393         
18394         
18395         
18396         el.update(html.join(""));
18397         this.nodes = el.dom.childNodes;
18398         this.updateIndexes(0);
18399     },
18400     
18401
18402     /**
18403      * Function to override to reformat the data that is sent to
18404      * the template for each node.
18405      * DEPRICATED - use the preparedata event handler.
18406      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18407      * a JSON object for an UpdateManager bound view).
18408      */
18409     prepareData : function(data, index, record)
18410     {
18411         this.fireEvent("preparedata", this, data, index, record);
18412         return data;
18413     },
18414
18415     onUpdate : function(ds, record){
18416         // Roo.log('on update');   
18417         this.clearSelections();
18418         var index = this.store.indexOf(record);
18419         var n = this.nodes[index];
18420         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18421         n.parentNode.removeChild(n);
18422         this.updateIndexes(index, index);
18423     },
18424
18425     
18426     
18427 // --------- FIXME     
18428     onAdd : function(ds, records, index)
18429     {
18430         //Roo.log(['on Add', ds, records, index] );        
18431         this.clearSelections();
18432         if(this.nodes.length == 0){
18433             this.refresh();
18434             return;
18435         }
18436         var n = this.nodes[index];
18437         for(var i = 0, len = records.length; i < len; i++){
18438             var d = this.prepareData(records[i].data, i, records[i]);
18439             if(n){
18440                 this.tpl.insertBefore(n, d);
18441             }else{
18442                 
18443                 this.tpl.append(this.el, d);
18444             }
18445         }
18446         this.updateIndexes(index);
18447     },
18448
18449     onRemove : function(ds, record, index){
18450        // Roo.log('onRemove');
18451         this.clearSelections();
18452         var el = this.dataName  ?
18453             this.el.child('.roo-tpl-' + this.dataName) :
18454             this.el; 
18455         
18456         el.dom.removeChild(this.nodes[index]);
18457         this.updateIndexes(index);
18458     },
18459
18460     /**
18461      * Refresh an individual node.
18462      * @param {Number} index
18463      */
18464     refreshNode : function(index){
18465         this.onUpdate(this.store, this.store.getAt(index));
18466     },
18467
18468     updateIndexes : function(startIndex, endIndex){
18469         var ns = this.nodes;
18470         startIndex = startIndex || 0;
18471         endIndex = endIndex || ns.length - 1;
18472         for(var i = startIndex; i <= endIndex; i++){
18473             ns[i].nodeIndex = i;
18474         }
18475     },
18476
18477     /**
18478      * Changes the data store this view uses and refresh the view.
18479      * @param {Store} store
18480      */
18481     setStore : function(store, initial){
18482         if(!initial && this.store){
18483             this.store.un("datachanged", this.refresh);
18484             this.store.un("add", this.onAdd);
18485             this.store.un("remove", this.onRemove);
18486             this.store.un("update", this.onUpdate);
18487             this.store.un("clear", this.refresh);
18488             this.store.un("beforeload", this.onBeforeLoad);
18489             this.store.un("load", this.onLoad);
18490             this.store.un("loadexception", this.onLoad);
18491         }
18492         if(store){
18493           
18494             store.on("datachanged", this.refresh, this);
18495             store.on("add", this.onAdd, this);
18496             store.on("remove", this.onRemove, this);
18497             store.on("update", this.onUpdate, this);
18498             store.on("clear", this.refresh, this);
18499             store.on("beforeload", this.onBeforeLoad, this);
18500             store.on("load", this.onLoad, this);
18501             store.on("loadexception", this.onLoad, this);
18502         }
18503         
18504         if(store){
18505             this.refresh();
18506         }
18507     },
18508     /**
18509      * onbeforeLoad - masks the loading area.
18510      *
18511      */
18512     onBeforeLoad : function(store,opts)
18513     {
18514          //Roo.log('onBeforeLoad');   
18515         if (!opts.add) {
18516             this.el.update("");
18517         }
18518         this.el.mask(this.mask ? this.mask : "Loading" ); 
18519     },
18520     onLoad : function ()
18521     {
18522         this.el.unmask();
18523     },
18524     
18525
18526     /**
18527      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18528      * @param {HTMLElement} node
18529      * @return {HTMLElement} The template node
18530      */
18531     findItemFromChild : function(node){
18532         var el = this.dataName  ?
18533             this.el.child('.roo-tpl-' + this.dataName,true) :
18534             this.el.dom; 
18535         
18536         if(!node || node.parentNode == el){
18537                     return node;
18538             }
18539             var p = node.parentNode;
18540             while(p && p != el){
18541             if(p.parentNode == el){
18542                 return p;
18543             }
18544             p = p.parentNode;
18545         }
18546             return null;
18547     },
18548
18549     /** @ignore */
18550     onClick : function(e){
18551         var item = this.findItemFromChild(e.getTarget());
18552         if(item){
18553             var index = this.indexOf(item);
18554             if(this.onItemClick(item, index, e) !== false){
18555                 this.fireEvent("click", this, index, item, e);
18556             }
18557         }else{
18558             this.clearSelections();
18559         }
18560     },
18561
18562     /** @ignore */
18563     onContextMenu : function(e){
18564         var item = this.findItemFromChild(e.getTarget());
18565         if(item){
18566             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18567         }
18568     },
18569
18570     /** @ignore */
18571     onDblClick : function(e){
18572         var item = this.findItemFromChild(e.getTarget());
18573         if(item){
18574             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18575         }
18576     },
18577
18578     onItemClick : function(item, index, e)
18579     {
18580         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18581             return false;
18582         }
18583         if (this.toggleSelect) {
18584             var m = this.isSelected(item) ? 'unselect' : 'select';
18585             //Roo.log(m);
18586             var _t = this;
18587             _t[m](item, true, false);
18588             return true;
18589         }
18590         if(this.multiSelect || this.singleSelect){
18591             if(this.multiSelect && e.shiftKey && this.lastSelection){
18592                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18593             }else{
18594                 this.select(item, this.multiSelect && e.ctrlKey);
18595                 this.lastSelection = item;
18596             }
18597             
18598             if(!this.tickable){
18599                 e.preventDefault();
18600             }
18601             
18602         }
18603         return true;
18604     },
18605
18606     /**
18607      * Get the number of selected nodes.
18608      * @return {Number}
18609      */
18610     getSelectionCount : function(){
18611         return this.selections.length;
18612     },
18613
18614     /**
18615      * Get the currently selected nodes.
18616      * @return {Array} An array of HTMLElements
18617      */
18618     getSelectedNodes : function(){
18619         return this.selections;
18620     },
18621
18622     /**
18623      * Get the indexes of the selected nodes.
18624      * @return {Array}
18625      */
18626     getSelectedIndexes : function(){
18627         var indexes = [], s = this.selections;
18628         for(var i = 0, len = s.length; i < len; i++){
18629             indexes.push(s[i].nodeIndex);
18630         }
18631         return indexes;
18632     },
18633
18634     /**
18635      * Clear all selections
18636      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18637      */
18638     clearSelections : function(suppressEvent){
18639         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18640             this.cmp.elements = this.selections;
18641             this.cmp.removeClass(this.selectedClass);
18642             this.selections = [];
18643             if(!suppressEvent){
18644                 this.fireEvent("selectionchange", this, this.selections);
18645             }
18646         }
18647     },
18648
18649     /**
18650      * Returns true if the passed node is selected
18651      * @param {HTMLElement/Number} node The node or node index
18652      * @return {Boolean}
18653      */
18654     isSelected : function(node){
18655         var s = this.selections;
18656         if(s.length < 1){
18657             return false;
18658         }
18659         node = this.getNode(node);
18660         return s.indexOf(node) !== -1;
18661     },
18662
18663     /**
18664      * Selects nodes.
18665      * @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
18666      * @param {Boolean} keepExisting (optional) true to keep existing selections
18667      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18668      */
18669     select : function(nodeInfo, keepExisting, suppressEvent){
18670         if(nodeInfo instanceof Array){
18671             if(!keepExisting){
18672                 this.clearSelections(true);
18673             }
18674             for(var i = 0, len = nodeInfo.length; i < len; i++){
18675                 this.select(nodeInfo[i], true, true);
18676             }
18677             return;
18678         } 
18679         var node = this.getNode(nodeInfo);
18680         if(!node || this.isSelected(node)){
18681             return; // already selected.
18682         }
18683         if(!keepExisting){
18684             this.clearSelections(true);
18685         }
18686         
18687         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18688             Roo.fly(node).addClass(this.selectedClass);
18689             this.selections.push(node);
18690             if(!suppressEvent){
18691                 this.fireEvent("selectionchange", this, this.selections);
18692             }
18693         }
18694         
18695         
18696     },
18697       /**
18698      * Unselects nodes.
18699      * @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
18700      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18701      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18702      */
18703     unselect : function(nodeInfo, keepExisting, suppressEvent)
18704     {
18705         if(nodeInfo instanceof Array){
18706             Roo.each(this.selections, function(s) {
18707                 this.unselect(s, nodeInfo);
18708             }, this);
18709             return;
18710         }
18711         var node = this.getNode(nodeInfo);
18712         if(!node || !this.isSelected(node)){
18713             //Roo.log("not selected");
18714             return; // not selected.
18715         }
18716         // fireevent???
18717         var ns = [];
18718         Roo.each(this.selections, function(s) {
18719             if (s == node ) {
18720                 Roo.fly(node).removeClass(this.selectedClass);
18721
18722                 return;
18723             }
18724             ns.push(s);
18725         },this);
18726         
18727         this.selections= ns;
18728         this.fireEvent("selectionchange", this, this.selections);
18729     },
18730
18731     /**
18732      * Gets a template node.
18733      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18734      * @return {HTMLElement} The node or null if it wasn't found
18735      */
18736     getNode : function(nodeInfo){
18737         if(typeof nodeInfo == "string"){
18738             return document.getElementById(nodeInfo);
18739         }else if(typeof nodeInfo == "number"){
18740             return this.nodes[nodeInfo];
18741         }
18742         return nodeInfo;
18743     },
18744
18745     /**
18746      * Gets a range template nodes.
18747      * @param {Number} startIndex
18748      * @param {Number} endIndex
18749      * @return {Array} An array of nodes
18750      */
18751     getNodes : function(start, end){
18752         var ns = this.nodes;
18753         start = start || 0;
18754         end = typeof end == "undefined" ? ns.length - 1 : end;
18755         var nodes = [];
18756         if(start <= end){
18757             for(var i = start; i <= end; i++){
18758                 nodes.push(ns[i]);
18759             }
18760         } else{
18761             for(var i = start; i >= end; i--){
18762                 nodes.push(ns[i]);
18763             }
18764         }
18765         return nodes;
18766     },
18767
18768     /**
18769      * Finds the index of the passed node
18770      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18771      * @return {Number} The index of the node or -1
18772      */
18773     indexOf : function(node){
18774         node = this.getNode(node);
18775         if(typeof node.nodeIndex == "number"){
18776             return node.nodeIndex;
18777         }
18778         var ns = this.nodes;
18779         for(var i = 0, len = ns.length; i < len; i++){
18780             if(ns[i] == node){
18781                 return i;
18782             }
18783         }
18784         return -1;
18785     }
18786 });
18787 /*
18788  * - LGPL
18789  *
18790  * based on jquery fullcalendar
18791  * 
18792  */
18793
18794 Roo.bootstrap = Roo.bootstrap || {};
18795 /**
18796  * @class Roo.bootstrap.Calendar
18797  * @extends Roo.bootstrap.Component
18798  * Bootstrap Calendar class
18799  * @cfg {Boolean} loadMask (true|false) default false
18800  * @cfg {Object} header generate the user specific header of the calendar, default false
18801
18802  * @constructor
18803  * Create a new Container
18804  * @param {Object} config The config object
18805  */
18806
18807
18808
18809 Roo.bootstrap.Calendar = function(config){
18810     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18811      this.addEvents({
18812         /**
18813              * @event select
18814              * Fires when a date is selected
18815              * @param {DatePicker} this
18816              * @param {Date} date The selected date
18817              */
18818         'select': true,
18819         /**
18820              * @event monthchange
18821              * Fires when the displayed month changes 
18822              * @param {DatePicker} this
18823              * @param {Date} date The selected month
18824              */
18825         'monthchange': true,
18826         /**
18827              * @event evententer
18828              * Fires when mouse over an event
18829              * @param {Calendar} this
18830              * @param {event} Event
18831              */
18832         'evententer': true,
18833         /**
18834              * @event eventleave
18835              * Fires when the mouse leaves an
18836              * @param {Calendar} this
18837              * @param {event}
18838              */
18839         'eventleave': true,
18840         /**
18841              * @event eventclick
18842              * Fires when the mouse click an
18843              * @param {Calendar} this
18844              * @param {event}
18845              */
18846         'eventclick': true
18847         
18848     });
18849
18850 };
18851
18852 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18853     
18854      /**
18855      * @cfg {Number} startDay
18856      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18857      */
18858     startDay : 0,
18859     
18860     loadMask : false,
18861     
18862     header : false,
18863       
18864     getAutoCreate : function(){
18865         
18866         
18867         var fc_button = function(name, corner, style, content ) {
18868             return Roo.apply({},{
18869                 tag : 'span',
18870                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18871                          (corner.length ?
18872                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18873                             ''
18874                         ),
18875                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18876                 unselectable: 'on'
18877             });
18878         };
18879         
18880         var header = {};
18881         
18882         if(!this.header){
18883             header = {
18884                 tag : 'table',
18885                 cls : 'fc-header',
18886                 style : 'width:100%',
18887                 cn : [
18888                     {
18889                         tag: 'tr',
18890                         cn : [
18891                             {
18892                                 tag : 'td',
18893                                 cls : 'fc-header-left',
18894                                 cn : [
18895                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18896                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18897                                     { tag: 'span', cls: 'fc-header-space' },
18898                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18899
18900
18901                                 ]
18902                             },
18903
18904                             {
18905                                 tag : 'td',
18906                                 cls : 'fc-header-center',
18907                                 cn : [
18908                                     {
18909                                         tag: 'span',
18910                                         cls: 'fc-header-title',
18911                                         cn : {
18912                                             tag: 'H2',
18913                                             html : 'month / year'
18914                                         }
18915                                     }
18916
18917                                 ]
18918                             },
18919                             {
18920                                 tag : 'td',
18921                                 cls : 'fc-header-right',
18922                                 cn : [
18923                               /*      fc_button('month', 'left', '', 'month' ),
18924                                     fc_button('week', '', '', 'week' ),
18925                                     fc_button('day', 'right', '', 'day' )
18926                                 */    
18927
18928                                 ]
18929                             }
18930
18931                         ]
18932                     }
18933                 ]
18934             };
18935         }
18936         
18937         header = this.header;
18938         
18939        
18940         var cal_heads = function() {
18941             var ret = [];
18942             // fixme - handle this.
18943             
18944             for (var i =0; i < Date.dayNames.length; i++) {
18945                 var d = Date.dayNames[i];
18946                 ret.push({
18947                     tag: 'th',
18948                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18949                     html : d.substring(0,3)
18950                 });
18951                 
18952             }
18953             ret[0].cls += ' fc-first';
18954             ret[6].cls += ' fc-last';
18955             return ret;
18956         };
18957         var cal_cell = function(n) {
18958             return  {
18959                 tag: 'td',
18960                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18961                 cn : [
18962                     {
18963                         cn : [
18964                             {
18965                                 cls: 'fc-day-number',
18966                                 html: 'D'
18967                             },
18968                             {
18969                                 cls: 'fc-day-content',
18970                              
18971                                 cn : [
18972                                      {
18973                                         style: 'position: relative;' // height: 17px;
18974                                     }
18975                                 ]
18976                             }
18977                             
18978                             
18979                         ]
18980                     }
18981                 ]
18982                 
18983             }
18984         };
18985         var cal_rows = function() {
18986             
18987             var ret = [];
18988             for (var r = 0; r < 6; r++) {
18989                 var row= {
18990                     tag : 'tr',
18991                     cls : 'fc-week',
18992                     cn : []
18993                 };
18994                 
18995                 for (var i =0; i < Date.dayNames.length; i++) {
18996                     var d = Date.dayNames[i];
18997                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18998
18999                 }
19000                 row.cn[0].cls+=' fc-first';
19001                 row.cn[0].cn[0].style = 'min-height:90px';
19002                 row.cn[6].cls+=' fc-last';
19003                 ret.push(row);
19004                 
19005             }
19006             ret[0].cls += ' fc-first';
19007             ret[4].cls += ' fc-prev-last';
19008             ret[5].cls += ' fc-last';
19009             return ret;
19010             
19011         };
19012         
19013         var cal_table = {
19014             tag: 'table',
19015             cls: 'fc-border-separate',
19016             style : 'width:100%',
19017             cellspacing  : 0,
19018             cn : [
19019                 { 
19020                     tag: 'thead',
19021                     cn : [
19022                         { 
19023                             tag: 'tr',
19024                             cls : 'fc-first fc-last',
19025                             cn : cal_heads()
19026                         }
19027                     ]
19028                 },
19029                 { 
19030                     tag: 'tbody',
19031                     cn : cal_rows()
19032                 }
19033                   
19034             ]
19035         };
19036          
19037          var cfg = {
19038             cls : 'fc fc-ltr',
19039             cn : [
19040                 header,
19041                 {
19042                     cls : 'fc-content',
19043                     style : "position: relative;",
19044                     cn : [
19045                         {
19046                             cls : 'fc-view fc-view-month fc-grid',
19047                             style : 'position: relative',
19048                             unselectable : 'on',
19049                             cn : [
19050                                 {
19051                                     cls : 'fc-event-container',
19052                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19053                                 },
19054                                 cal_table
19055                             ]
19056                         }
19057                     ]
19058     
19059                 }
19060            ] 
19061             
19062         };
19063         
19064          
19065         
19066         return cfg;
19067     },
19068     
19069     
19070     initEvents : function()
19071     {
19072         if(!this.store){
19073             throw "can not find store for calendar";
19074         }
19075         
19076         var mark = {
19077             tag: "div",
19078             cls:"x-dlg-mask",
19079             style: "text-align:center",
19080             cn: [
19081                 {
19082                     tag: "div",
19083                     style: "background-color:white;width:50%;margin:250 auto",
19084                     cn: [
19085                         {
19086                             tag: "img",
19087                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19088                         },
19089                         {
19090                             tag: "span",
19091                             html: "Loading"
19092                         }
19093                         
19094                     ]
19095                 }
19096             ]
19097         };
19098         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19099         
19100         var size = this.el.select('.fc-content', true).first().getSize();
19101         this.maskEl.setSize(size.width, size.height);
19102         this.maskEl.enableDisplayMode("block");
19103         if(!this.loadMask){
19104             this.maskEl.hide();
19105         }
19106         
19107         this.store = Roo.factory(this.store, Roo.data);
19108         this.store.on('load', this.onLoad, this);
19109         this.store.on('beforeload', this.onBeforeLoad, this);
19110         
19111         this.resize();
19112         
19113         this.cells = this.el.select('.fc-day',true);
19114         //Roo.log(this.cells);
19115         this.textNodes = this.el.query('.fc-day-number');
19116         this.cells.addClassOnOver('fc-state-hover');
19117         
19118         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19119         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19120         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19121         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19122         
19123         this.on('monthchange', this.onMonthChange, this);
19124         
19125         this.update(new Date().clearTime());
19126     },
19127     
19128     resize : function() {
19129         var sz  = this.el.getSize();
19130         
19131         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19132         this.el.select('.fc-day-content div',true).setHeight(34);
19133     },
19134     
19135     
19136     // private
19137     showPrevMonth : function(e){
19138         this.update(this.activeDate.add("mo", -1));
19139     },
19140     showToday : function(e){
19141         this.update(new Date().clearTime());
19142     },
19143     // private
19144     showNextMonth : function(e){
19145         this.update(this.activeDate.add("mo", 1));
19146     },
19147
19148     // private
19149     showPrevYear : function(){
19150         this.update(this.activeDate.add("y", -1));
19151     },
19152
19153     // private
19154     showNextYear : function(){
19155         this.update(this.activeDate.add("y", 1));
19156     },
19157
19158     
19159    // private
19160     update : function(date)
19161     {
19162         var vd = this.activeDate;
19163         this.activeDate = date;
19164 //        if(vd && this.el){
19165 //            var t = date.getTime();
19166 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19167 //                Roo.log('using add remove');
19168 //                
19169 //                this.fireEvent('monthchange', this, date);
19170 //                
19171 //                this.cells.removeClass("fc-state-highlight");
19172 //                this.cells.each(function(c){
19173 //                   if(c.dateValue == t){
19174 //                       c.addClass("fc-state-highlight");
19175 //                       setTimeout(function(){
19176 //                            try{c.dom.firstChild.focus();}catch(e){}
19177 //                       }, 50);
19178 //                       return false;
19179 //                   }
19180 //                   return true;
19181 //                });
19182 //                return;
19183 //            }
19184 //        }
19185         
19186         var days = date.getDaysInMonth();
19187         
19188         var firstOfMonth = date.getFirstDateOfMonth();
19189         var startingPos = firstOfMonth.getDay()-this.startDay;
19190         
19191         if(startingPos < this.startDay){
19192             startingPos += 7;
19193         }
19194         
19195         var pm = date.add(Date.MONTH, -1);
19196         var prevStart = pm.getDaysInMonth()-startingPos;
19197 //        
19198         this.cells = this.el.select('.fc-day',true);
19199         this.textNodes = this.el.query('.fc-day-number');
19200         this.cells.addClassOnOver('fc-state-hover');
19201         
19202         var cells = this.cells.elements;
19203         var textEls = this.textNodes;
19204         
19205         Roo.each(cells, function(cell){
19206             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19207         });
19208         
19209         days += startingPos;
19210
19211         // convert everything to numbers so it's fast
19212         var day = 86400000;
19213         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19214         //Roo.log(d);
19215         //Roo.log(pm);
19216         //Roo.log(prevStart);
19217         
19218         var today = new Date().clearTime().getTime();
19219         var sel = date.clearTime().getTime();
19220         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19221         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19222         var ddMatch = this.disabledDatesRE;
19223         var ddText = this.disabledDatesText;
19224         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19225         var ddaysText = this.disabledDaysText;
19226         var format = this.format;
19227         
19228         var setCellClass = function(cal, cell){
19229             cell.row = 0;
19230             cell.events = [];
19231             cell.more = [];
19232             //Roo.log('set Cell Class');
19233             cell.title = "";
19234             var t = d.getTime();
19235             
19236             //Roo.log(d);
19237             
19238             cell.dateValue = t;
19239             if(t == today){
19240                 cell.className += " fc-today";
19241                 cell.className += " fc-state-highlight";
19242                 cell.title = cal.todayText;
19243             }
19244             if(t == sel){
19245                 // disable highlight in other month..
19246                 //cell.className += " fc-state-highlight";
19247                 
19248             }
19249             // disabling
19250             if(t < min) {
19251                 cell.className = " fc-state-disabled";
19252                 cell.title = cal.minText;
19253                 return;
19254             }
19255             if(t > max) {
19256                 cell.className = " fc-state-disabled";
19257                 cell.title = cal.maxText;
19258                 return;
19259             }
19260             if(ddays){
19261                 if(ddays.indexOf(d.getDay()) != -1){
19262                     cell.title = ddaysText;
19263                     cell.className = " fc-state-disabled";
19264                 }
19265             }
19266             if(ddMatch && format){
19267                 var fvalue = d.dateFormat(format);
19268                 if(ddMatch.test(fvalue)){
19269                     cell.title = ddText.replace("%0", fvalue);
19270                     cell.className = " fc-state-disabled";
19271                 }
19272             }
19273             
19274             if (!cell.initialClassName) {
19275                 cell.initialClassName = cell.dom.className;
19276             }
19277             
19278             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19279         };
19280
19281         var i = 0;
19282         
19283         for(; i < startingPos; i++) {
19284             textEls[i].innerHTML = (++prevStart);
19285             d.setDate(d.getDate()+1);
19286             
19287             cells[i].className = "fc-past fc-other-month";
19288             setCellClass(this, cells[i]);
19289         }
19290         
19291         var intDay = 0;
19292         
19293         for(; i < days; i++){
19294             intDay = i - startingPos + 1;
19295             textEls[i].innerHTML = (intDay);
19296             d.setDate(d.getDate()+1);
19297             
19298             cells[i].className = ''; // "x-date-active";
19299             setCellClass(this, cells[i]);
19300         }
19301         var extraDays = 0;
19302         
19303         for(; i < 42; i++) {
19304             textEls[i].innerHTML = (++extraDays);
19305             d.setDate(d.getDate()+1);
19306             
19307             cells[i].className = "fc-future fc-other-month";
19308             setCellClass(this, cells[i]);
19309         }
19310         
19311         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19312         
19313         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19314         
19315         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19316         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19317         
19318         if(totalRows != 6){
19319             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19320             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19321         }
19322         
19323         this.fireEvent('monthchange', this, date);
19324         
19325         
19326         /*
19327         if(!this.internalRender){
19328             var main = this.el.dom.firstChild;
19329             var w = main.offsetWidth;
19330             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19331             Roo.fly(main).setWidth(w);
19332             this.internalRender = true;
19333             // opera does not respect the auto grow header center column
19334             // then, after it gets a width opera refuses to recalculate
19335             // without a second pass
19336             if(Roo.isOpera && !this.secondPass){
19337                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19338                 this.secondPass = true;
19339                 this.update.defer(10, this, [date]);
19340             }
19341         }
19342         */
19343         
19344     },
19345     
19346     findCell : function(dt) {
19347         dt = dt.clearTime().getTime();
19348         var ret = false;
19349         this.cells.each(function(c){
19350             //Roo.log("check " +c.dateValue + '?=' + dt);
19351             if(c.dateValue == dt){
19352                 ret = c;
19353                 return false;
19354             }
19355             return true;
19356         });
19357         
19358         return ret;
19359     },
19360     
19361     findCells : function(ev) {
19362         var s = ev.start.clone().clearTime().getTime();
19363        // Roo.log(s);
19364         var e= ev.end.clone().clearTime().getTime();
19365        // Roo.log(e);
19366         var ret = [];
19367         this.cells.each(function(c){
19368              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19369             
19370             if(c.dateValue > e){
19371                 return ;
19372             }
19373             if(c.dateValue < s){
19374                 return ;
19375             }
19376             ret.push(c);
19377         });
19378         
19379         return ret;    
19380     },
19381     
19382 //    findBestRow: function(cells)
19383 //    {
19384 //        var ret = 0;
19385 //        
19386 //        for (var i =0 ; i < cells.length;i++) {
19387 //            ret  = Math.max(cells[i].rows || 0,ret);
19388 //        }
19389 //        return ret;
19390 //        
19391 //    },
19392     
19393     
19394     addItem : function(ev)
19395     {
19396         // look for vertical location slot in
19397         var cells = this.findCells(ev);
19398         
19399 //        ev.row = this.findBestRow(cells);
19400         
19401         // work out the location.
19402         
19403         var crow = false;
19404         var rows = [];
19405         for(var i =0; i < cells.length; i++) {
19406             
19407             cells[i].row = cells[0].row;
19408             
19409             if(i == 0){
19410                 cells[i].row = cells[i].row + 1;
19411             }
19412             
19413             if (!crow) {
19414                 crow = {
19415                     start : cells[i],
19416                     end :  cells[i]
19417                 };
19418                 continue;
19419             }
19420             if (crow.start.getY() == cells[i].getY()) {
19421                 // on same row.
19422                 crow.end = cells[i];
19423                 continue;
19424             }
19425             // different row.
19426             rows.push(crow);
19427             crow = {
19428                 start: cells[i],
19429                 end : cells[i]
19430             };
19431             
19432         }
19433         
19434         rows.push(crow);
19435         ev.els = [];
19436         ev.rows = rows;
19437         ev.cells = cells;
19438         
19439         cells[0].events.push(ev);
19440         
19441         this.calevents.push(ev);
19442     },
19443     
19444     clearEvents: function() {
19445         
19446         if(!this.calevents){
19447             return;
19448         }
19449         
19450         Roo.each(this.cells.elements, function(c){
19451             c.row = 0;
19452             c.events = [];
19453             c.more = [];
19454         });
19455         
19456         Roo.each(this.calevents, function(e) {
19457             Roo.each(e.els, function(el) {
19458                 el.un('mouseenter' ,this.onEventEnter, this);
19459                 el.un('mouseleave' ,this.onEventLeave, this);
19460                 el.remove();
19461             },this);
19462         },this);
19463         
19464         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19465             e.remove();
19466         });
19467         
19468     },
19469     
19470     renderEvents: function()
19471     {   
19472         var _this = this;
19473         
19474         this.cells.each(function(c) {
19475             
19476             if(c.row < 5){
19477                 return;
19478             }
19479             
19480             var ev = c.events;
19481             
19482             var r = 4;
19483             if(c.row != c.events.length){
19484                 r = 4 - (4 - (c.row - c.events.length));
19485             }
19486             
19487             c.events = ev.slice(0, r);
19488             c.more = ev.slice(r);
19489             
19490             if(c.more.length && c.more.length == 1){
19491                 c.events.push(c.more.pop());
19492             }
19493             
19494             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19495             
19496         });
19497             
19498         this.cells.each(function(c) {
19499             
19500             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19501             
19502             
19503             for (var e = 0; e < c.events.length; e++){
19504                 var ev = c.events[e];
19505                 var rows = ev.rows;
19506                 
19507                 for(var i = 0; i < rows.length; i++) {
19508                 
19509                     // how many rows should it span..
19510
19511                     var  cfg = {
19512                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19513                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19514
19515                         unselectable : "on",
19516                         cn : [
19517                             {
19518                                 cls: 'fc-event-inner',
19519                                 cn : [
19520     //                                {
19521     //                                  tag:'span',
19522     //                                  cls: 'fc-event-time',
19523     //                                  html : cells.length > 1 ? '' : ev.time
19524     //                                },
19525                                     {
19526                                       tag:'span',
19527                                       cls: 'fc-event-title',
19528                                       html : String.format('{0}', ev.title)
19529                                     }
19530
19531
19532                                 ]
19533                             },
19534                             {
19535                                 cls: 'ui-resizable-handle ui-resizable-e',
19536                                 html : '&nbsp;&nbsp;&nbsp'
19537                             }
19538
19539                         ]
19540                     };
19541
19542                     if (i == 0) {
19543                         cfg.cls += ' fc-event-start';
19544                     }
19545                     if ((i+1) == rows.length) {
19546                         cfg.cls += ' fc-event-end';
19547                     }
19548
19549                     var ctr = _this.el.select('.fc-event-container',true).first();
19550                     var cg = ctr.createChild(cfg);
19551
19552                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19553                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19554
19555                     var r = (c.more.length) ? 1 : 0;
19556                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19557                     cg.setWidth(ebox.right - sbox.x -2);
19558
19559                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19560                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19561                     cg.on('click', _this.onEventClick, _this, ev);
19562
19563                     ev.els.push(cg);
19564                     
19565                 }
19566                 
19567             }
19568             
19569             
19570             if(c.more.length){
19571                 var  cfg = {
19572                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19573                     style : 'position: absolute',
19574                     unselectable : "on",
19575                     cn : [
19576                         {
19577                             cls: 'fc-event-inner',
19578                             cn : [
19579                                 {
19580                                   tag:'span',
19581                                   cls: 'fc-event-title',
19582                                   html : 'More'
19583                                 }
19584
19585
19586                             ]
19587                         },
19588                         {
19589                             cls: 'ui-resizable-handle ui-resizable-e',
19590                             html : '&nbsp;&nbsp;&nbsp'
19591                         }
19592
19593                     ]
19594                 };
19595
19596                 var ctr = _this.el.select('.fc-event-container',true).first();
19597                 var cg = ctr.createChild(cfg);
19598
19599                 var sbox = c.select('.fc-day-content',true).first().getBox();
19600                 var ebox = c.select('.fc-day-content',true).first().getBox();
19601                 //Roo.log(cg);
19602                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19603                 cg.setWidth(ebox.right - sbox.x -2);
19604
19605                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19606                 
19607             }
19608             
19609         });
19610         
19611         
19612         
19613     },
19614     
19615     onEventEnter: function (e, el,event,d) {
19616         this.fireEvent('evententer', this, el, event);
19617     },
19618     
19619     onEventLeave: function (e, el,event,d) {
19620         this.fireEvent('eventleave', this, el, event);
19621     },
19622     
19623     onEventClick: function (e, el,event,d) {
19624         this.fireEvent('eventclick', this, el, event);
19625     },
19626     
19627     onMonthChange: function () {
19628         this.store.load();
19629     },
19630     
19631     onMoreEventClick: function(e, el, more)
19632     {
19633         var _this = this;
19634         
19635         this.calpopover.placement = 'right';
19636         this.calpopover.setTitle('More');
19637         
19638         this.calpopover.setContent('');
19639         
19640         var ctr = this.calpopover.el.select('.popover-content', true).first();
19641         
19642         Roo.each(more, function(m){
19643             var cfg = {
19644                 cls : 'fc-event-hori fc-event-draggable',
19645                 html : m.title
19646             };
19647             var cg = ctr.createChild(cfg);
19648             
19649             cg.on('click', _this.onEventClick, _this, m);
19650         });
19651         
19652         this.calpopover.show(el);
19653         
19654         
19655     },
19656     
19657     onLoad: function () 
19658     {   
19659         this.calevents = [];
19660         var cal = this;
19661         
19662         if(this.store.getCount() > 0){
19663             this.store.data.each(function(d){
19664                cal.addItem({
19665                     id : d.data.id,
19666                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19667                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19668                     time : d.data.start_time,
19669                     title : d.data.title,
19670                     description : d.data.description,
19671                     venue : d.data.venue
19672                 });
19673             });
19674         }
19675         
19676         this.renderEvents();
19677         
19678         if(this.calevents.length && this.loadMask){
19679             this.maskEl.hide();
19680         }
19681     },
19682     
19683     onBeforeLoad: function()
19684     {
19685         this.clearEvents();
19686         if(this.loadMask){
19687             this.maskEl.show();
19688         }
19689     }
19690 });
19691
19692  
19693  /*
19694  * - LGPL
19695  *
19696  * element
19697  * 
19698  */
19699
19700 /**
19701  * @class Roo.bootstrap.Popover
19702  * @extends Roo.bootstrap.Component
19703  * Bootstrap Popover class
19704  * @cfg {String} html contents of the popover   (or false to use children..)
19705  * @cfg {String} title of popover (or false to hide)
19706  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19707  * @cfg {String} trigger click || hover (or false to trigger manually)
19708  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19709  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19710  *      - if false and it has a 'parent' then it will be automatically added to that element
19711  *      - if string - Roo.get  will be called 
19712  * @cfg {Number} delay - delay before showing
19713  
19714  * @constructor
19715  * Create a new Popover
19716  * @param {Object} config The config object
19717  */
19718
19719 Roo.bootstrap.Popover = function(config){
19720     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19721     
19722     this.addEvents({
19723         // raw events
19724          /**
19725          * @event show
19726          * After the popover show
19727          * 
19728          * @param {Roo.bootstrap.Popover} this
19729          */
19730         "show" : true,
19731         /**
19732          * @event hide
19733          * After the popover hide
19734          * 
19735          * @param {Roo.bootstrap.Popover} this
19736          */
19737         "hide" : true
19738     });
19739 };
19740
19741 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19742     
19743     title: false,
19744     html: false,
19745     
19746     placement : 'right',
19747     trigger : 'hover', // hover
19748     modal : false,
19749     delay : 0,
19750     
19751     over: false,
19752     
19753     can_build_overlaid : false,
19754     
19755     maskEl : false, // the mask element
19756     headerEl : false,
19757     contentEl : false,
19758     alignEl : false, // when show is called with an element - this get's stored.
19759     
19760     getChildContainer : function()
19761     {
19762         return this.contentEl;
19763         
19764     },
19765     getPopoverHeader : function()
19766     {
19767         this.title = true; // flag not to hide it..
19768         this.headerEl.addClass('p-0');
19769         return this.headerEl
19770     },
19771     
19772     
19773     getAutoCreate : function(){
19774          
19775         var cfg = {
19776            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19777            style: 'display:block',
19778            cn : [
19779                 {
19780                     cls : 'arrow'
19781                 },
19782                 {
19783                     cls : 'popover-inner ',
19784                     cn : [
19785                         {
19786                             tag: 'h3',
19787                             cls: 'popover-title popover-header',
19788                             html : this.title === false ? '' : this.title
19789                         },
19790                         {
19791                             cls : 'popover-content popover-body '  + (this.cls || ''),
19792                             html : this.html || ''
19793                         }
19794                     ]
19795                     
19796                 }
19797            ]
19798         };
19799         
19800         return cfg;
19801     },
19802     /**
19803      * @param {string} the title
19804      */
19805     setTitle: function(str)
19806     {
19807         this.title = str;
19808         if (this.el) {
19809             this.headerEl.dom.innerHTML = str;
19810         }
19811         
19812     },
19813     /**
19814      * @param {string} the body content
19815      */
19816     setContent: function(str)
19817     {
19818         this.html = str;
19819         if (this.contentEl) {
19820             this.contentEl.dom.innerHTML = str;
19821         }
19822         
19823     },
19824     // as it get's added to the bottom of the page.
19825     onRender : function(ct, position)
19826     {
19827         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19828         
19829         
19830         
19831         if(!this.el){
19832             var cfg = Roo.apply({},  this.getAutoCreate());
19833             cfg.id = Roo.id();
19834             
19835             if (this.cls) {
19836                 cfg.cls += ' ' + this.cls;
19837             }
19838             if (this.style) {
19839                 cfg.style = this.style;
19840             }
19841             //Roo.log("adding to ");
19842             this.el = Roo.get(document.body).createChild(cfg, position);
19843 //            Roo.log(this.el);
19844         }
19845         
19846         this.contentEl = this.el.select('.popover-content',true).first();
19847         this.headerEl =  this.el.select('.popover-title',true).first();
19848         
19849         var nitems = [];
19850         if(typeof(this.items) != 'undefined'){
19851             var items = this.items;
19852             delete this.items;
19853
19854             for(var i =0;i < items.length;i++) {
19855                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19856             }
19857         }
19858
19859         this.items = nitems;
19860         
19861         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19862         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19863         
19864         
19865         
19866         this.initEvents();
19867     },
19868     
19869     resizeMask : function()
19870     {
19871         this.maskEl.setSize(
19872             Roo.lib.Dom.getViewWidth(true),
19873             Roo.lib.Dom.getViewHeight(true)
19874         );
19875     },
19876     
19877     initEvents : function()
19878     {
19879         
19880         if (!this.modal) { 
19881             Roo.bootstrap.Popover.register(this);
19882         }
19883          
19884         this.arrowEl = this.el.select('.arrow',true).first();
19885         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19886         this.el.enableDisplayMode('block');
19887         this.el.hide();
19888  
19889         
19890         if (this.over === false && !this.parent()) {
19891             return; 
19892         }
19893         if (this.triggers === false) {
19894             return;
19895         }
19896          
19897         // support parent
19898         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19899         var triggers = this.trigger ? this.trigger.split(' ') : [];
19900         Roo.each(triggers, function(trigger) {
19901         
19902             if (trigger == 'click') {
19903                 on_el.on('click', this.toggle, this);
19904             } else if (trigger != 'manual') {
19905                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19906                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19907       
19908                 on_el.on(eventIn  ,this.enter, this);
19909                 on_el.on(eventOut, this.leave, this);
19910             }
19911         }, this);
19912     },
19913     
19914     
19915     // private
19916     timeout : null,
19917     hoverState : null,
19918     
19919     toggle : function () {
19920         this.hoverState == 'in' ? this.leave() : this.enter();
19921     },
19922     
19923     enter : function () {
19924         
19925         clearTimeout(this.timeout);
19926     
19927         this.hoverState = 'in';
19928     
19929         if (!this.delay || !this.delay.show) {
19930             this.show();
19931             return;
19932         }
19933         var _t = this;
19934         this.timeout = setTimeout(function () {
19935             if (_t.hoverState == 'in') {
19936                 _t.show();
19937             }
19938         }, this.delay.show)
19939     },
19940     
19941     leave : function() {
19942         clearTimeout(this.timeout);
19943     
19944         this.hoverState = 'out';
19945     
19946         if (!this.delay || !this.delay.hide) {
19947             this.hide();
19948             return;
19949         }
19950         var _t = this;
19951         this.timeout = setTimeout(function () {
19952             if (_t.hoverState == 'out') {
19953                 _t.hide();
19954             }
19955         }, this.delay.hide)
19956     },
19957     /**
19958      * Show the popover
19959      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19960      * @param {string} (left|right|top|bottom) position
19961      */
19962     show : function (on_el, placement)
19963     {
19964         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19965         on_el = on_el || false; // default to false
19966          
19967         if (!on_el) {
19968             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19969                 on_el = this.parent().el;
19970             } else if (this.over) {
19971                 Roo.get(this.over);
19972             }
19973             
19974         }
19975         
19976         this.alignEl = Roo.get( on_el );
19977
19978         if (!this.el) {
19979             this.render(document.body);
19980         }
19981         
19982         
19983          
19984         
19985         if (this.title === false) {
19986             this.headerEl.hide();
19987         }
19988         
19989        
19990         this.el.show();
19991         this.el.dom.style.display = 'block';
19992          
19993  
19994         if (this.alignEl) {
19995             this.updatePosition(this.placement, true);
19996              
19997         } else {
19998             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19999             var es = this.el.getSize();
20000             var x = Roo.lib.Dom.getViewWidth()/2;
20001             var y = Roo.lib.Dom.getViewHeight()/2;
20002             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20003             
20004         }
20005
20006         
20007         //var arrow = this.el.select('.arrow',true).first();
20008         //arrow.set(align[2], 
20009         
20010         this.el.addClass('in');
20011         
20012          
20013         
20014         this.hoverState = 'in';
20015         
20016         if (this.modal) {
20017             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20018             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20019             this.maskEl.dom.style.display = 'block';
20020             this.maskEl.addClass('show');
20021         }
20022         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20023  
20024         this.fireEvent('show', this);
20025         
20026     },
20027     /**
20028      * fire this manually after loading a grid in the table for example
20029      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20030      * @param {Boolean} try and move it if we cant get right position.
20031      */
20032     updatePosition : function(placement, try_move)
20033     {
20034         // allow for calling with no parameters
20035         placement = placement   ? placement :  this.placement;
20036         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20037         
20038         this.el.removeClass([
20039             'fade','top','bottom', 'left', 'right','in',
20040             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20041         ]);
20042         this.el.addClass(placement + ' bs-popover-' + placement);
20043         
20044         if (!this.alignEl ) {
20045             return false;
20046         }
20047         
20048         switch (placement) {
20049             case 'right':
20050                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20051                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20052                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20053                     //normal display... or moved up/down.
20054                     this.el.setXY(offset);
20055                     var xy = this.alignEl.getAnchorXY('tr', false);
20056                     xy[0]+=2;xy[1]+=5;
20057                     this.arrowEl.setXY(xy);
20058                     return true;
20059                 }
20060                 // continue through...
20061                 return this.updatePosition('left', false);
20062                 
20063             
20064             case 'left':
20065                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20066                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20067                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20068                     //normal display... or moved up/down.
20069                     this.el.setXY(offset);
20070                     var xy = this.alignEl.getAnchorXY('tl', false);
20071                     xy[0]-=10;xy[1]+=5; // << fix me
20072                     this.arrowEl.setXY(xy);
20073                     return true;
20074                 }
20075                 // call self...
20076                 return this.updatePosition('right', false);
20077             
20078             case 'top':
20079                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20080                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20081                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20082                     //normal display... or moved up/down.
20083                     this.el.setXY(offset);
20084                     var xy = this.alignEl.getAnchorXY('t', false);
20085                     xy[1]-=10; // << fix me
20086                     this.arrowEl.setXY(xy);
20087                     return true;
20088                 }
20089                 // fall through
20090                return this.updatePosition('bottom', false);
20091             
20092             case 'bottom':
20093                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20094                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20095                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20096                     //normal display... or moved up/down.
20097                     this.el.setXY(offset);
20098                     var xy = this.alignEl.getAnchorXY('b', false);
20099                      xy[1]+=2; // << fix me
20100                     this.arrowEl.setXY(xy);
20101                     return true;
20102                 }
20103                 // fall through
20104                 return this.updatePosition('top', false);
20105                 
20106             
20107         }
20108         
20109         
20110         return false;
20111     },
20112     
20113     hide : function()
20114     {
20115         this.el.setXY([0,0]);
20116         this.el.removeClass('in');
20117         this.el.hide();
20118         this.hoverState = null;
20119         this.maskEl.hide(); // always..
20120         this.fireEvent('hide', this);
20121     }
20122     
20123 });
20124
20125
20126 Roo.apply(Roo.bootstrap.Popover, {
20127
20128     alignment : {
20129         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20130         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20131         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20132         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20133     },
20134     
20135     zIndex : 20001,
20136
20137     clickHander : false,
20138     
20139
20140     onMouseDown : function(e)
20141     {
20142         if (!e.getTarget(".roo-popover")) {
20143             this.hideAll();
20144         }
20145          
20146     },
20147     
20148     popups : [],
20149     
20150     register : function(popup)
20151     {
20152         if (!Roo.bootstrap.Popover.clickHandler) {
20153             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20154         }
20155         // hide other popups.
20156         this.hideAll();
20157         this.popups.push(popup);
20158     },
20159     hideAll : function()
20160     {
20161         this.popups.forEach(function(p) {
20162             p.hide();
20163         });
20164     }
20165
20166 });/*
20167  * - LGPL
20168  *
20169  * Card header - holder for the card header elements.
20170  * 
20171  */
20172
20173 /**
20174  * @class Roo.bootstrap.PopoverNav
20175  * @extends Roo.bootstrap.NavGroup
20176  * Bootstrap Popover header navigation class
20177  * @constructor
20178  * Create a new Popover Header Navigation 
20179  * @param {Object} config The config object
20180  */
20181
20182 Roo.bootstrap.PopoverNav = function(config){
20183     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20184 };
20185
20186 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20187     
20188     
20189     container_method : 'getPopoverHeader' 
20190     
20191      
20192     
20193     
20194    
20195 });
20196
20197  
20198
20199  /*
20200  * - LGPL
20201  *
20202  * Progress
20203  * 
20204  */
20205
20206 /**
20207  * @class Roo.bootstrap.Progress
20208  * @extends Roo.bootstrap.Component
20209  * Bootstrap Progress class
20210  * @cfg {Boolean} striped striped of the progress bar
20211  * @cfg {Boolean} active animated of the progress bar
20212  * 
20213  * 
20214  * @constructor
20215  * Create a new Progress
20216  * @param {Object} config The config object
20217  */
20218
20219 Roo.bootstrap.Progress = function(config){
20220     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20221 };
20222
20223 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20224     
20225     striped : false,
20226     active: false,
20227     
20228     getAutoCreate : function(){
20229         var cfg = {
20230             tag: 'div',
20231             cls: 'progress'
20232         };
20233         
20234         
20235         if(this.striped){
20236             cfg.cls += ' progress-striped';
20237         }
20238       
20239         if(this.active){
20240             cfg.cls += ' active';
20241         }
20242         
20243         
20244         return cfg;
20245     }
20246    
20247 });
20248
20249  
20250
20251  /*
20252  * - LGPL
20253  *
20254  * ProgressBar
20255  * 
20256  */
20257
20258 /**
20259  * @class Roo.bootstrap.ProgressBar
20260  * @extends Roo.bootstrap.Component
20261  * Bootstrap ProgressBar class
20262  * @cfg {Number} aria_valuenow aria-value now
20263  * @cfg {Number} aria_valuemin aria-value min
20264  * @cfg {Number} aria_valuemax aria-value max
20265  * @cfg {String} label label for the progress bar
20266  * @cfg {String} panel (success | info | warning | danger )
20267  * @cfg {String} role role of the progress bar
20268  * @cfg {String} sr_only text
20269  * 
20270  * 
20271  * @constructor
20272  * Create a new ProgressBar
20273  * @param {Object} config The config object
20274  */
20275
20276 Roo.bootstrap.ProgressBar = function(config){
20277     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20278 };
20279
20280 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20281     
20282     aria_valuenow : 0,
20283     aria_valuemin : 0,
20284     aria_valuemax : 100,
20285     label : false,
20286     panel : false,
20287     role : false,
20288     sr_only: false,
20289     
20290     getAutoCreate : function()
20291     {
20292         
20293         var cfg = {
20294             tag: 'div',
20295             cls: 'progress-bar',
20296             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20297         };
20298         
20299         if(this.sr_only){
20300             cfg.cn = {
20301                 tag: 'span',
20302                 cls: 'sr-only',
20303                 html: this.sr_only
20304             }
20305         }
20306         
20307         if(this.role){
20308             cfg.role = this.role;
20309         }
20310         
20311         if(this.aria_valuenow){
20312             cfg['aria-valuenow'] = this.aria_valuenow;
20313         }
20314         
20315         if(this.aria_valuemin){
20316             cfg['aria-valuemin'] = this.aria_valuemin;
20317         }
20318         
20319         if(this.aria_valuemax){
20320             cfg['aria-valuemax'] = this.aria_valuemax;
20321         }
20322         
20323         if(this.label && !this.sr_only){
20324             cfg.html = this.label;
20325         }
20326         
20327         if(this.panel){
20328             cfg.cls += ' progress-bar-' + this.panel;
20329         }
20330         
20331         return cfg;
20332     },
20333     
20334     update : function(aria_valuenow)
20335     {
20336         this.aria_valuenow = aria_valuenow;
20337         
20338         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20339     }
20340    
20341 });
20342
20343  
20344
20345  /*
20346  * - LGPL
20347  *
20348  * column
20349  * 
20350  */
20351
20352 /**
20353  * @class Roo.bootstrap.TabGroup
20354  * @extends Roo.bootstrap.Column
20355  * Bootstrap Column class
20356  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20357  * @cfg {Boolean} carousel true to make the group behave like a carousel
20358  * @cfg {Boolean} bullets show bullets for the panels
20359  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20360  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20361  * @cfg {Boolean} showarrow (true|false) show arrow default true
20362  * 
20363  * @constructor
20364  * Create a new TabGroup
20365  * @param {Object} config The config object
20366  */
20367
20368 Roo.bootstrap.TabGroup = function(config){
20369     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20370     if (!this.navId) {
20371         this.navId = Roo.id();
20372     }
20373     this.tabs = [];
20374     Roo.bootstrap.TabGroup.register(this);
20375     
20376 };
20377
20378 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20379     
20380     carousel : false,
20381     transition : false,
20382     bullets : 0,
20383     timer : 0,
20384     autoslide : false,
20385     slideFn : false,
20386     slideOnTouch : false,
20387     showarrow : true,
20388     
20389     getAutoCreate : function()
20390     {
20391         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20392         
20393         cfg.cls += ' tab-content';
20394         
20395         if (this.carousel) {
20396             cfg.cls += ' carousel slide';
20397             
20398             cfg.cn = [{
20399                cls : 'carousel-inner',
20400                cn : []
20401             }];
20402         
20403             if(this.bullets  && !Roo.isTouch){
20404                 
20405                 var bullets = {
20406                     cls : 'carousel-bullets',
20407                     cn : []
20408                 };
20409                
20410                 if(this.bullets_cls){
20411                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20412                 }
20413                 
20414                 bullets.cn.push({
20415                     cls : 'clear'
20416                 });
20417                 
20418                 cfg.cn[0].cn.push(bullets);
20419             }
20420             
20421             if(this.showarrow){
20422                 cfg.cn[0].cn.push({
20423                     tag : 'div',
20424                     class : 'carousel-arrow',
20425                     cn : [
20426                         {
20427                             tag : 'div',
20428                             class : 'carousel-prev',
20429                             cn : [
20430                                 {
20431                                     tag : 'i',
20432                                     class : 'fa fa-chevron-left'
20433                                 }
20434                             ]
20435                         },
20436                         {
20437                             tag : 'div',
20438                             class : 'carousel-next',
20439                             cn : [
20440                                 {
20441                                     tag : 'i',
20442                                     class : 'fa fa-chevron-right'
20443                                 }
20444                             ]
20445                         }
20446                     ]
20447                 });
20448             }
20449             
20450         }
20451         
20452         return cfg;
20453     },
20454     
20455     initEvents:  function()
20456     {
20457 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20458 //            this.el.on("touchstart", this.onTouchStart, this);
20459 //        }
20460         
20461         if(this.autoslide){
20462             var _this = this;
20463             
20464             this.slideFn = window.setInterval(function() {
20465                 _this.showPanelNext();
20466             }, this.timer);
20467         }
20468         
20469         if(this.showarrow){
20470             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20471             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20472         }
20473         
20474         
20475     },
20476     
20477 //    onTouchStart : function(e, el, o)
20478 //    {
20479 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20480 //            return;
20481 //        }
20482 //        
20483 //        this.showPanelNext();
20484 //    },
20485     
20486     
20487     getChildContainer : function()
20488     {
20489         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20490     },
20491     
20492     /**
20493     * register a Navigation item
20494     * @param {Roo.bootstrap.NavItem} the navitem to add
20495     */
20496     register : function(item)
20497     {
20498         this.tabs.push( item);
20499         item.navId = this.navId; // not really needed..
20500         this.addBullet();
20501     
20502     },
20503     
20504     getActivePanel : function()
20505     {
20506         var r = false;
20507         Roo.each(this.tabs, function(t) {
20508             if (t.active) {
20509                 r = t;
20510                 return false;
20511             }
20512             return null;
20513         });
20514         return r;
20515         
20516     },
20517     getPanelByName : function(n)
20518     {
20519         var r = false;
20520         Roo.each(this.tabs, function(t) {
20521             if (t.tabId == n) {
20522                 r = t;
20523                 return false;
20524             }
20525             return null;
20526         });
20527         return r;
20528     },
20529     indexOfPanel : function(p)
20530     {
20531         var r = false;
20532         Roo.each(this.tabs, function(t,i) {
20533             if (t.tabId == p.tabId) {
20534                 r = i;
20535                 return false;
20536             }
20537             return null;
20538         });
20539         return r;
20540     },
20541     /**
20542      * show a specific panel
20543      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20544      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20545      */
20546     showPanel : function (pan)
20547     {
20548         if(this.transition || typeof(pan) == 'undefined'){
20549             Roo.log("waiting for the transitionend");
20550             return false;
20551         }
20552         
20553         if (typeof(pan) == 'number') {
20554             pan = this.tabs[pan];
20555         }
20556         
20557         if (typeof(pan) == 'string') {
20558             pan = this.getPanelByName(pan);
20559         }
20560         
20561         var cur = this.getActivePanel();
20562         
20563         if(!pan || !cur){
20564             Roo.log('pan or acitve pan is undefined');
20565             return false;
20566         }
20567         
20568         if (pan.tabId == this.getActivePanel().tabId) {
20569             return true;
20570         }
20571         
20572         if (false === cur.fireEvent('beforedeactivate')) {
20573             return false;
20574         }
20575         
20576         if(this.bullets > 0 && !Roo.isTouch){
20577             this.setActiveBullet(this.indexOfPanel(pan));
20578         }
20579         
20580         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20581             
20582             //class="carousel-item carousel-item-next carousel-item-left"
20583             
20584             this.transition = true;
20585             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20586             var lr = dir == 'next' ? 'left' : 'right';
20587             pan.el.addClass(dir); // or prev
20588             pan.el.addClass('carousel-item-' + dir); // or prev
20589             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20590             cur.el.addClass(lr); // or right
20591             pan.el.addClass(lr);
20592             cur.el.addClass('carousel-item-' +lr); // or right
20593             pan.el.addClass('carousel-item-' +lr);
20594             
20595             
20596             var _this = this;
20597             cur.el.on('transitionend', function() {
20598                 Roo.log("trans end?");
20599                 
20600                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20601                 pan.setActive(true);
20602                 
20603                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20604                 cur.setActive(false);
20605                 
20606                 _this.transition = false;
20607                 
20608             }, this, { single:  true } );
20609             
20610             return true;
20611         }
20612         
20613         cur.setActive(false);
20614         pan.setActive(true);
20615         
20616         return true;
20617         
20618     },
20619     showPanelNext : function()
20620     {
20621         var i = this.indexOfPanel(this.getActivePanel());
20622         
20623         if (i >= this.tabs.length - 1 && !this.autoslide) {
20624             return;
20625         }
20626         
20627         if (i >= this.tabs.length - 1 && this.autoslide) {
20628             i = -1;
20629         }
20630         
20631         this.showPanel(this.tabs[i+1]);
20632     },
20633     
20634     showPanelPrev : function()
20635     {
20636         var i = this.indexOfPanel(this.getActivePanel());
20637         
20638         if (i  < 1 && !this.autoslide) {
20639             return;
20640         }
20641         
20642         if (i < 1 && this.autoslide) {
20643             i = this.tabs.length;
20644         }
20645         
20646         this.showPanel(this.tabs[i-1]);
20647     },
20648     
20649     
20650     addBullet: function()
20651     {
20652         if(!this.bullets || Roo.isTouch){
20653             return;
20654         }
20655         var ctr = this.el.select('.carousel-bullets',true).first();
20656         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20657         var bullet = ctr.createChild({
20658             cls : 'bullet bullet-' + i
20659         },ctr.dom.lastChild);
20660         
20661         
20662         var _this = this;
20663         
20664         bullet.on('click', (function(e, el, o, ii, t){
20665
20666             e.preventDefault();
20667
20668             this.showPanel(ii);
20669
20670             if(this.autoslide && this.slideFn){
20671                 clearInterval(this.slideFn);
20672                 this.slideFn = window.setInterval(function() {
20673                     _this.showPanelNext();
20674                 }, this.timer);
20675             }
20676
20677         }).createDelegate(this, [i, bullet], true));
20678                 
20679         
20680     },
20681      
20682     setActiveBullet : function(i)
20683     {
20684         if(Roo.isTouch){
20685             return;
20686         }
20687         
20688         Roo.each(this.el.select('.bullet', true).elements, function(el){
20689             el.removeClass('selected');
20690         });
20691
20692         var bullet = this.el.select('.bullet-' + i, true).first();
20693         
20694         if(!bullet){
20695             return;
20696         }
20697         
20698         bullet.addClass('selected');
20699     }
20700     
20701     
20702   
20703 });
20704
20705  
20706
20707  
20708  
20709 Roo.apply(Roo.bootstrap.TabGroup, {
20710     
20711     groups: {},
20712      /**
20713     * register a Navigation Group
20714     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20715     */
20716     register : function(navgrp)
20717     {
20718         this.groups[navgrp.navId] = navgrp;
20719         
20720     },
20721     /**
20722     * fetch a Navigation Group based on the navigation ID
20723     * if one does not exist , it will get created.
20724     * @param {string} the navgroup to add
20725     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20726     */
20727     get: function(navId) {
20728         if (typeof(this.groups[navId]) == 'undefined') {
20729             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20730         }
20731         return this.groups[navId] ;
20732     }
20733     
20734     
20735     
20736 });
20737
20738  /*
20739  * - LGPL
20740  *
20741  * TabPanel
20742  * 
20743  */
20744
20745 /**
20746  * @class Roo.bootstrap.TabPanel
20747  * @extends Roo.bootstrap.Component
20748  * Bootstrap TabPanel class
20749  * @cfg {Boolean} active panel active
20750  * @cfg {String} html panel content
20751  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20752  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20753  * @cfg {String} href click to link..
20754  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20755  * 
20756  * 
20757  * @constructor
20758  * Create a new TabPanel
20759  * @param {Object} config The config object
20760  */
20761
20762 Roo.bootstrap.TabPanel = function(config){
20763     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20764     this.addEvents({
20765         /**
20766              * @event changed
20767              * Fires when the active status changes
20768              * @param {Roo.bootstrap.TabPanel} this
20769              * @param {Boolean} state the new state
20770             
20771          */
20772         'changed': true,
20773         /**
20774              * @event beforedeactivate
20775              * Fires before a tab is de-activated - can be used to do validation on a form.
20776              * @param {Roo.bootstrap.TabPanel} this
20777              * @return {Boolean} false if there is an error
20778             
20779          */
20780         'beforedeactivate': true
20781      });
20782     
20783     this.tabId = this.tabId || Roo.id();
20784   
20785 };
20786
20787 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20788     
20789     active: false,
20790     html: false,
20791     tabId: false,
20792     navId : false,
20793     href : '',
20794     touchSlide : false,
20795     getAutoCreate : function(){
20796         
20797         
20798         var cfg = {
20799             tag: 'div',
20800             // item is needed for carousel - not sure if it has any effect otherwise
20801             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20802             html: this.html || ''
20803         };
20804         
20805         if(this.active){
20806             cfg.cls += ' active';
20807         }
20808         
20809         if(this.tabId){
20810             cfg.tabId = this.tabId;
20811         }
20812         
20813         
20814         
20815         return cfg;
20816     },
20817     
20818     initEvents:  function()
20819     {
20820         var p = this.parent();
20821         
20822         this.navId = this.navId || p.navId;
20823         
20824         if (typeof(this.navId) != 'undefined') {
20825             // not really needed.. but just in case.. parent should be a NavGroup.
20826             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20827             
20828             tg.register(this);
20829             
20830             var i = tg.tabs.length - 1;
20831             
20832             if(this.active && tg.bullets > 0 && i < tg.bullets){
20833                 tg.setActiveBullet(i);
20834             }
20835         }
20836         
20837         this.el.on('click', this.onClick, this);
20838         
20839         if(Roo.isTouch && this.touchSlide){
20840             this.el.on("touchstart", this.onTouchStart, this);
20841             this.el.on("touchmove", this.onTouchMove, this);
20842             this.el.on("touchend", this.onTouchEnd, this);
20843         }
20844         
20845     },
20846     
20847     onRender : function(ct, position)
20848     {
20849         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20850     },
20851     
20852     setActive : function(state)
20853     {
20854         Roo.log("panel - set active " + this.tabId + "=" + state);
20855         
20856         this.active = state;
20857         if (!state) {
20858             this.el.removeClass('active');
20859             
20860         } else  if (!this.el.hasClass('active')) {
20861             this.el.addClass('active');
20862         }
20863         
20864         this.fireEvent('changed', this, state);
20865     },
20866     
20867     onClick : function(e)
20868     {
20869         e.preventDefault();
20870         
20871         if(!this.href.length){
20872             return;
20873         }
20874         
20875         window.location.href = this.href;
20876     },
20877     
20878     startX : 0,
20879     startY : 0,
20880     endX : 0,
20881     endY : 0,
20882     swiping : false,
20883     
20884     onTouchStart : function(e)
20885     {
20886         this.swiping = false;
20887         
20888         this.startX = e.browserEvent.touches[0].clientX;
20889         this.startY = e.browserEvent.touches[0].clientY;
20890     },
20891     
20892     onTouchMove : function(e)
20893     {
20894         this.swiping = true;
20895         
20896         this.endX = e.browserEvent.touches[0].clientX;
20897         this.endY = e.browserEvent.touches[0].clientY;
20898     },
20899     
20900     onTouchEnd : function(e)
20901     {
20902         if(!this.swiping){
20903             this.onClick(e);
20904             return;
20905         }
20906         
20907         var tabGroup = this.parent();
20908         
20909         if(this.endX > this.startX){ // swiping right
20910             tabGroup.showPanelPrev();
20911             return;
20912         }
20913         
20914         if(this.startX > this.endX){ // swiping left
20915             tabGroup.showPanelNext();
20916             return;
20917         }
20918     }
20919     
20920     
20921 });
20922  
20923
20924  
20925
20926  /*
20927  * - LGPL
20928  *
20929  * DateField
20930  * 
20931  */
20932
20933 /**
20934  * @class Roo.bootstrap.DateField
20935  * @extends Roo.bootstrap.Input
20936  * Bootstrap DateField class
20937  * @cfg {Number} weekStart default 0
20938  * @cfg {String} viewMode default empty, (months|years)
20939  * @cfg {String} minViewMode default empty, (months|years)
20940  * @cfg {Number} startDate default -Infinity
20941  * @cfg {Number} endDate default Infinity
20942  * @cfg {Boolean} todayHighlight default false
20943  * @cfg {Boolean} todayBtn default false
20944  * @cfg {Boolean} calendarWeeks default false
20945  * @cfg {Object} daysOfWeekDisabled default empty
20946  * @cfg {Boolean} singleMode default false (true | false)
20947  * 
20948  * @cfg {Boolean} keyboardNavigation default true
20949  * @cfg {String} language default en
20950  * 
20951  * @constructor
20952  * Create a new DateField
20953  * @param {Object} config The config object
20954  */
20955
20956 Roo.bootstrap.DateField = function(config){
20957     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20958      this.addEvents({
20959             /**
20960              * @event show
20961              * Fires when this field show.
20962              * @param {Roo.bootstrap.DateField} this
20963              * @param {Mixed} date The date value
20964              */
20965             show : true,
20966             /**
20967              * @event show
20968              * Fires when this field hide.
20969              * @param {Roo.bootstrap.DateField} this
20970              * @param {Mixed} date The date value
20971              */
20972             hide : true,
20973             /**
20974              * @event select
20975              * Fires when select a date.
20976              * @param {Roo.bootstrap.DateField} this
20977              * @param {Mixed} date The date value
20978              */
20979             select : true,
20980             /**
20981              * @event beforeselect
20982              * Fires when before select a date.
20983              * @param {Roo.bootstrap.DateField} this
20984              * @param {Mixed} date The date value
20985              */
20986             beforeselect : true
20987         });
20988 };
20989
20990 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20991     
20992     /**
20993      * @cfg {String} format
20994      * The default date format string which can be overriden for localization support.  The format must be
20995      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20996      */
20997     format : "m/d/y",
20998     /**
20999      * @cfg {String} altFormats
21000      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21001      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21002      */
21003     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21004     
21005     weekStart : 0,
21006     
21007     viewMode : '',
21008     
21009     minViewMode : '',
21010     
21011     todayHighlight : false,
21012     
21013     todayBtn: false,
21014     
21015     language: 'en',
21016     
21017     keyboardNavigation: true,
21018     
21019     calendarWeeks: false,
21020     
21021     startDate: -Infinity,
21022     
21023     endDate: Infinity,
21024     
21025     daysOfWeekDisabled: [],
21026     
21027     _events: [],
21028     
21029     singleMode : false,
21030     
21031     UTCDate: function()
21032     {
21033         return new Date(Date.UTC.apply(Date, arguments));
21034     },
21035     
21036     UTCToday: function()
21037     {
21038         var today = new Date();
21039         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21040     },
21041     
21042     getDate: function() {
21043             var d = this.getUTCDate();
21044             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21045     },
21046     
21047     getUTCDate: function() {
21048             return this.date;
21049     },
21050     
21051     setDate: function(d) {
21052             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21053     },
21054     
21055     setUTCDate: function(d) {
21056             this.date = d;
21057             this.setValue(this.formatDate(this.date));
21058     },
21059         
21060     onRender: function(ct, position)
21061     {
21062         
21063         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21064         
21065         this.language = this.language || 'en';
21066         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21067         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21068         
21069         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21070         this.format = this.format || 'm/d/y';
21071         this.isInline = false;
21072         this.isInput = true;
21073         this.component = this.el.select('.add-on', true).first() || false;
21074         this.component = (this.component && this.component.length === 0) ? false : this.component;
21075         this.hasInput = this.component && this.inputEl().length;
21076         
21077         if (typeof(this.minViewMode === 'string')) {
21078             switch (this.minViewMode) {
21079                 case 'months':
21080                     this.minViewMode = 1;
21081                     break;
21082                 case 'years':
21083                     this.minViewMode = 2;
21084                     break;
21085                 default:
21086                     this.minViewMode = 0;
21087                     break;
21088             }
21089         }
21090         
21091         if (typeof(this.viewMode === 'string')) {
21092             switch (this.viewMode) {
21093                 case 'months':
21094                     this.viewMode = 1;
21095                     break;
21096                 case 'years':
21097                     this.viewMode = 2;
21098                     break;
21099                 default:
21100                     this.viewMode = 0;
21101                     break;
21102             }
21103         }
21104                 
21105         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21106         
21107 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21108         
21109         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21110         
21111         this.picker().on('mousedown', this.onMousedown, this);
21112         this.picker().on('click', this.onClick, this);
21113         
21114         this.picker().addClass('datepicker-dropdown');
21115         
21116         this.startViewMode = this.viewMode;
21117         
21118         if(this.singleMode){
21119             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21120                 v.setVisibilityMode(Roo.Element.DISPLAY);
21121                 v.hide();
21122             });
21123             
21124             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21125                 v.setStyle('width', '189px');
21126             });
21127         }
21128         
21129         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21130             if(!this.calendarWeeks){
21131                 v.remove();
21132                 return;
21133             }
21134             
21135             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21136             v.attr('colspan', function(i, val){
21137                 return parseInt(val) + 1;
21138             });
21139         });
21140                         
21141         
21142         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21143         
21144         this.setStartDate(this.startDate);
21145         this.setEndDate(this.endDate);
21146         
21147         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21148         
21149         this.fillDow();
21150         this.fillMonths();
21151         this.update();
21152         this.showMode();
21153         
21154         if(this.isInline) {
21155             this.showPopup();
21156         }
21157     },
21158     
21159     picker : function()
21160     {
21161         return this.pickerEl;
21162 //        return this.el.select('.datepicker', true).first();
21163     },
21164     
21165     fillDow: function()
21166     {
21167         var dowCnt = this.weekStart;
21168         
21169         var dow = {
21170             tag: 'tr',
21171             cn: [
21172                 
21173             ]
21174         };
21175         
21176         if(this.calendarWeeks){
21177             dow.cn.push({
21178                 tag: 'th',
21179                 cls: 'cw',
21180                 html: '&nbsp;'
21181             })
21182         }
21183         
21184         while (dowCnt < this.weekStart + 7) {
21185             dow.cn.push({
21186                 tag: 'th',
21187                 cls: 'dow',
21188                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21189             });
21190         }
21191         
21192         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21193     },
21194     
21195     fillMonths: function()
21196     {    
21197         var i = 0;
21198         var months = this.picker().select('>.datepicker-months td', true).first();
21199         
21200         months.dom.innerHTML = '';
21201         
21202         while (i < 12) {
21203             var month = {
21204                 tag: 'span',
21205                 cls: 'month',
21206                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21207             };
21208             
21209             months.createChild(month);
21210         }
21211         
21212     },
21213     
21214     update: function()
21215     {
21216         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;
21217         
21218         if (this.date < this.startDate) {
21219             this.viewDate = new Date(this.startDate);
21220         } else if (this.date > this.endDate) {
21221             this.viewDate = new Date(this.endDate);
21222         } else {
21223             this.viewDate = new Date(this.date);
21224         }
21225         
21226         this.fill();
21227     },
21228     
21229     fill: function() 
21230     {
21231         var d = new Date(this.viewDate),
21232                 year = d.getUTCFullYear(),
21233                 month = d.getUTCMonth(),
21234                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21235                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21236                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21237                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21238                 currentDate = this.date && this.date.valueOf(),
21239                 today = this.UTCToday();
21240         
21241         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21242         
21243 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21244         
21245 //        this.picker.select('>tfoot th.today').
21246 //                                              .text(dates[this.language].today)
21247 //                                              .toggle(this.todayBtn !== false);
21248     
21249         this.updateNavArrows();
21250         this.fillMonths();
21251                                                 
21252         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21253         
21254         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21255          
21256         prevMonth.setUTCDate(day);
21257         
21258         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21259         
21260         var nextMonth = new Date(prevMonth);
21261         
21262         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21263         
21264         nextMonth = nextMonth.valueOf();
21265         
21266         var fillMonths = false;
21267         
21268         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21269         
21270         while(prevMonth.valueOf() <= nextMonth) {
21271             var clsName = '';
21272             
21273             if (prevMonth.getUTCDay() === this.weekStart) {
21274                 if(fillMonths){
21275                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21276                 }
21277                     
21278                 fillMonths = {
21279                     tag: 'tr',
21280                     cn: []
21281                 };
21282                 
21283                 if(this.calendarWeeks){
21284                     // ISO 8601: First week contains first thursday.
21285                     // ISO also states week starts on Monday, but we can be more abstract here.
21286                     var
21287                     // Start of current week: based on weekstart/current date
21288                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21289                     // Thursday of this week
21290                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21291                     // First Thursday of year, year from thursday
21292                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21293                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21294                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21295                     
21296                     fillMonths.cn.push({
21297                         tag: 'td',
21298                         cls: 'cw',
21299                         html: calWeek
21300                     });
21301                 }
21302             }
21303             
21304             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21305                 clsName += ' old';
21306             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21307                 clsName += ' new';
21308             }
21309             if (this.todayHighlight &&
21310                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21311                 prevMonth.getUTCMonth() == today.getMonth() &&
21312                 prevMonth.getUTCDate() == today.getDate()) {
21313                 clsName += ' today';
21314             }
21315             
21316             if (currentDate && prevMonth.valueOf() === currentDate) {
21317                 clsName += ' active';
21318             }
21319             
21320             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21321                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21322                     clsName += ' disabled';
21323             }
21324             
21325             fillMonths.cn.push({
21326                 tag: 'td',
21327                 cls: 'day ' + clsName,
21328                 html: prevMonth.getDate()
21329             });
21330             
21331             prevMonth.setDate(prevMonth.getDate()+1);
21332         }
21333           
21334         var currentYear = this.date && this.date.getUTCFullYear();
21335         var currentMonth = this.date && this.date.getUTCMonth();
21336         
21337         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21338         
21339         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21340             v.removeClass('active');
21341             
21342             if(currentYear === year && k === currentMonth){
21343                 v.addClass('active');
21344             }
21345             
21346             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21347                 v.addClass('disabled');
21348             }
21349             
21350         });
21351         
21352         
21353         year = parseInt(year/10, 10) * 10;
21354         
21355         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21356         
21357         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21358         
21359         year -= 1;
21360         for (var i = -1; i < 11; i++) {
21361             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21362                 tag: 'span',
21363                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21364                 html: year
21365             });
21366             
21367             year += 1;
21368         }
21369     },
21370     
21371     showMode: function(dir) 
21372     {
21373         if (dir) {
21374             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21375         }
21376         
21377         Roo.each(this.picker().select('>div',true).elements, function(v){
21378             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21379             v.hide();
21380         });
21381         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21382     },
21383     
21384     place: function()
21385     {
21386         if(this.isInline) {
21387             return;
21388         }
21389         
21390         this.picker().removeClass(['bottom', 'top']);
21391         
21392         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21393             /*
21394              * place to the top of element!
21395              *
21396              */
21397             
21398             this.picker().addClass('top');
21399             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21400             
21401             return;
21402         }
21403         
21404         this.picker().addClass('bottom');
21405         
21406         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21407     },
21408     
21409     parseDate : function(value)
21410     {
21411         if(!value || value instanceof Date){
21412             return value;
21413         }
21414         var v = Date.parseDate(value, this.format);
21415         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21416             v = Date.parseDate(value, 'Y-m-d');
21417         }
21418         if(!v && this.altFormats){
21419             if(!this.altFormatsArray){
21420                 this.altFormatsArray = this.altFormats.split("|");
21421             }
21422             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21423                 v = Date.parseDate(value, this.altFormatsArray[i]);
21424             }
21425         }
21426         return v;
21427     },
21428     
21429     formatDate : function(date, fmt)
21430     {   
21431         return (!date || !(date instanceof Date)) ?
21432         date : date.dateFormat(fmt || this.format);
21433     },
21434     
21435     onFocus : function()
21436     {
21437         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21438         this.showPopup();
21439     },
21440     
21441     onBlur : function()
21442     {
21443         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21444         
21445         var d = this.inputEl().getValue();
21446         
21447         this.setValue(d);
21448                 
21449         this.hidePopup();
21450     },
21451     
21452     showPopup : function()
21453     {
21454         this.picker().show();
21455         this.update();
21456         this.place();
21457         
21458         this.fireEvent('showpopup', this, this.date);
21459     },
21460     
21461     hidePopup : function()
21462     {
21463         if(this.isInline) {
21464             return;
21465         }
21466         this.picker().hide();
21467         this.viewMode = this.startViewMode;
21468         this.showMode();
21469         
21470         this.fireEvent('hidepopup', this, this.date);
21471         
21472     },
21473     
21474     onMousedown: function(e)
21475     {
21476         e.stopPropagation();
21477         e.preventDefault();
21478     },
21479     
21480     keyup: function(e)
21481     {
21482         Roo.bootstrap.DateField.superclass.keyup.call(this);
21483         this.update();
21484     },
21485
21486     setValue: function(v)
21487     {
21488         if(this.fireEvent('beforeselect', this, v) !== false){
21489             var d = new Date(this.parseDate(v) ).clearTime();
21490         
21491             if(isNaN(d.getTime())){
21492                 this.date = this.viewDate = '';
21493                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21494                 return;
21495             }
21496
21497             v = this.formatDate(d);
21498
21499             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21500
21501             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21502
21503             this.update();
21504
21505             this.fireEvent('select', this, this.date);
21506         }
21507     },
21508     
21509     getValue: function()
21510     {
21511         return this.formatDate(this.date);
21512     },
21513     
21514     fireKey: function(e)
21515     {
21516         if (!this.picker().isVisible()){
21517             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21518                 this.showPopup();
21519             }
21520             return;
21521         }
21522         
21523         var dateChanged = false,
21524         dir, day, month,
21525         newDate, newViewDate;
21526         
21527         switch(e.keyCode){
21528             case 27: // escape
21529                 this.hidePopup();
21530                 e.preventDefault();
21531                 break;
21532             case 37: // left
21533             case 39: // right
21534                 if (!this.keyboardNavigation) {
21535                     break;
21536                 }
21537                 dir = e.keyCode == 37 ? -1 : 1;
21538                 
21539                 if (e.ctrlKey){
21540                     newDate = this.moveYear(this.date, dir);
21541                     newViewDate = this.moveYear(this.viewDate, dir);
21542                 } else if (e.shiftKey){
21543                     newDate = this.moveMonth(this.date, dir);
21544                     newViewDate = this.moveMonth(this.viewDate, dir);
21545                 } else {
21546                     newDate = new Date(this.date);
21547                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21548                     newViewDate = new Date(this.viewDate);
21549                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21550                 }
21551                 if (this.dateWithinRange(newDate)){
21552                     this.date = newDate;
21553                     this.viewDate = newViewDate;
21554                     this.setValue(this.formatDate(this.date));
21555 //                    this.update();
21556                     e.preventDefault();
21557                     dateChanged = true;
21558                 }
21559                 break;
21560             case 38: // up
21561             case 40: // down
21562                 if (!this.keyboardNavigation) {
21563                     break;
21564                 }
21565                 dir = e.keyCode == 38 ? -1 : 1;
21566                 if (e.ctrlKey){
21567                     newDate = this.moveYear(this.date, dir);
21568                     newViewDate = this.moveYear(this.viewDate, dir);
21569                 } else if (e.shiftKey){
21570                     newDate = this.moveMonth(this.date, dir);
21571                     newViewDate = this.moveMonth(this.viewDate, dir);
21572                 } else {
21573                     newDate = new Date(this.date);
21574                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21575                     newViewDate = new Date(this.viewDate);
21576                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21577                 }
21578                 if (this.dateWithinRange(newDate)){
21579                     this.date = newDate;
21580                     this.viewDate = newViewDate;
21581                     this.setValue(this.formatDate(this.date));
21582 //                    this.update();
21583                     e.preventDefault();
21584                     dateChanged = true;
21585                 }
21586                 break;
21587             case 13: // enter
21588                 this.setValue(this.formatDate(this.date));
21589                 this.hidePopup();
21590                 e.preventDefault();
21591                 break;
21592             case 9: // tab
21593                 this.setValue(this.formatDate(this.date));
21594                 this.hidePopup();
21595                 break;
21596             case 16: // shift
21597             case 17: // ctrl
21598             case 18: // alt
21599                 break;
21600             default :
21601                 this.hidePopup();
21602                 
21603         }
21604     },
21605     
21606     
21607     onClick: function(e) 
21608     {
21609         e.stopPropagation();
21610         e.preventDefault();
21611         
21612         var target = e.getTarget();
21613         
21614         if(target.nodeName.toLowerCase() === 'i'){
21615             target = Roo.get(target).dom.parentNode;
21616         }
21617         
21618         var nodeName = target.nodeName;
21619         var className = target.className;
21620         var html = target.innerHTML;
21621         //Roo.log(nodeName);
21622         
21623         switch(nodeName.toLowerCase()) {
21624             case 'th':
21625                 switch(className) {
21626                     case 'switch':
21627                         this.showMode(1);
21628                         break;
21629                     case 'prev':
21630                     case 'next':
21631                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21632                         switch(this.viewMode){
21633                                 case 0:
21634                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21635                                         break;
21636                                 case 1:
21637                                 case 2:
21638                                         this.viewDate = this.moveYear(this.viewDate, dir);
21639                                         break;
21640                         }
21641                         this.fill();
21642                         break;
21643                     case 'today':
21644                         var date = new Date();
21645                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21646 //                        this.fill()
21647                         this.setValue(this.formatDate(this.date));
21648                         
21649                         this.hidePopup();
21650                         break;
21651                 }
21652                 break;
21653             case 'span':
21654                 if (className.indexOf('disabled') < 0) {
21655                     this.viewDate.setUTCDate(1);
21656                     if (className.indexOf('month') > -1) {
21657                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21658                     } else {
21659                         var year = parseInt(html, 10) || 0;
21660                         this.viewDate.setUTCFullYear(year);
21661                         
21662                     }
21663                     
21664                     if(this.singleMode){
21665                         this.setValue(this.formatDate(this.viewDate));
21666                         this.hidePopup();
21667                         return;
21668                     }
21669                     
21670                     this.showMode(-1);
21671                     this.fill();
21672                 }
21673                 break;
21674                 
21675             case 'td':
21676                 //Roo.log(className);
21677                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21678                     var day = parseInt(html, 10) || 1;
21679                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21680                         month = (this.viewDate || new Date()).getUTCMonth();
21681
21682                     if (className.indexOf('old') > -1) {
21683                         if(month === 0 ){
21684                             month = 11;
21685                             year -= 1;
21686                         }else{
21687                             month -= 1;
21688                         }
21689                     } else if (className.indexOf('new') > -1) {
21690                         if (month == 11) {
21691                             month = 0;
21692                             year += 1;
21693                         } else {
21694                             month += 1;
21695                         }
21696                     }
21697                     //Roo.log([year,month,day]);
21698                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21699                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21700 //                    this.fill();
21701                     //Roo.log(this.formatDate(this.date));
21702                     this.setValue(this.formatDate(this.date));
21703                     this.hidePopup();
21704                 }
21705                 break;
21706         }
21707     },
21708     
21709     setStartDate: function(startDate)
21710     {
21711         this.startDate = startDate || -Infinity;
21712         if (this.startDate !== -Infinity) {
21713             this.startDate = this.parseDate(this.startDate);
21714         }
21715         this.update();
21716         this.updateNavArrows();
21717     },
21718
21719     setEndDate: function(endDate)
21720     {
21721         this.endDate = endDate || Infinity;
21722         if (this.endDate !== Infinity) {
21723             this.endDate = this.parseDate(this.endDate);
21724         }
21725         this.update();
21726         this.updateNavArrows();
21727     },
21728     
21729     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21730     {
21731         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21732         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21733             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21734         }
21735         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21736             return parseInt(d, 10);
21737         });
21738         this.update();
21739         this.updateNavArrows();
21740     },
21741     
21742     updateNavArrows: function() 
21743     {
21744         if(this.singleMode){
21745             return;
21746         }
21747         
21748         var d = new Date(this.viewDate),
21749         year = d.getUTCFullYear(),
21750         month = d.getUTCMonth();
21751         
21752         Roo.each(this.picker().select('.prev', true).elements, function(v){
21753             v.show();
21754             switch (this.viewMode) {
21755                 case 0:
21756
21757                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21758                         v.hide();
21759                     }
21760                     break;
21761                 case 1:
21762                 case 2:
21763                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21764                         v.hide();
21765                     }
21766                     break;
21767             }
21768         });
21769         
21770         Roo.each(this.picker().select('.next', true).elements, function(v){
21771             v.show();
21772             switch (this.viewMode) {
21773                 case 0:
21774
21775                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21776                         v.hide();
21777                     }
21778                     break;
21779                 case 1:
21780                 case 2:
21781                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21782                         v.hide();
21783                     }
21784                     break;
21785             }
21786         })
21787     },
21788     
21789     moveMonth: function(date, dir)
21790     {
21791         if (!dir) {
21792             return date;
21793         }
21794         var new_date = new Date(date.valueOf()),
21795         day = new_date.getUTCDate(),
21796         month = new_date.getUTCMonth(),
21797         mag = Math.abs(dir),
21798         new_month, test;
21799         dir = dir > 0 ? 1 : -1;
21800         if (mag == 1){
21801             test = dir == -1
21802             // If going back one month, make sure month is not current month
21803             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21804             ? function(){
21805                 return new_date.getUTCMonth() == month;
21806             }
21807             // If going forward one month, make sure month is as expected
21808             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21809             : function(){
21810                 return new_date.getUTCMonth() != new_month;
21811             };
21812             new_month = month + dir;
21813             new_date.setUTCMonth(new_month);
21814             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21815             if (new_month < 0 || new_month > 11) {
21816                 new_month = (new_month + 12) % 12;
21817             }
21818         } else {
21819             // For magnitudes >1, move one month at a time...
21820             for (var i=0; i<mag; i++) {
21821                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21822                 new_date = this.moveMonth(new_date, dir);
21823             }
21824             // ...then reset the day, keeping it in the new month
21825             new_month = new_date.getUTCMonth();
21826             new_date.setUTCDate(day);
21827             test = function(){
21828                 return new_month != new_date.getUTCMonth();
21829             };
21830         }
21831         // Common date-resetting loop -- if date is beyond end of month, make it
21832         // end of month
21833         while (test()){
21834             new_date.setUTCDate(--day);
21835             new_date.setUTCMonth(new_month);
21836         }
21837         return new_date;
21838     },
21839
21840     moveYear: function(date, dir)
21841     {
21842         return this.moveMonth(date, dir*12);
21843     },
21844
21845     dateWithinRange: function(date)
21846     {
21847         return date >= this.startDate && date <= this.endDate;
21848     },
21849
21850     
21851     remove: function() 
21852     {
21853         this.picker().remove();
21854     },
21855     
21856     validateValue : function(value)
21857     {
21858         if(this.getVisibilityEl().hasClass('hidden')){
21859             return true;
21860         }
21861         
21862         if(value.length < 1)  {
21863             if(this.allowBlank){
21864                 return true;
21865             }
21866             return false;
21867         }
21868         
21869         if(value.length < this.minLength){
21870             return false;
21871         }
21872         if(value.length > this.maxLength){
21873             return false;
21874         }
21875         if(this.vtype){
21876             var vt = Roo.form.VTypes;
21877             if(!vt[this.vtype](value, this)){
21878                 return false;
21879             }
21880         }
21881         if(typeof this.validator == "function"){
21882             var msg = this.validator(value);
21883             if(msg !== true){
21884                 return false;
21885             }
21886         }
21887         
21888         if(this.regex && !this.regex.test(value)){
21889             return false;
21890         }
21891         
21892         if(typeof(this.parseDate(value)) == 'undefined'){
21893             return false;
21894         }
21895         
21896         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21897             return false;
21898         }      
21899         
21900         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21901             return false;
21902         } 
21903         
21904         
21905         return true;
21906     },
21907     
21908     reset : function()
21909     {
21910         this.date = this.viewDate = '';
21911         
21912         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21913     }
21914    
21915 });
21916
21917 Roo.apply(Roo.bootstrap.DateField,  {
21918     
21919     head : {
21920         tag: 'thead',
21921         cn: [
21922         {
21923             tag: 'tr',
21924             cn: [
21925             {
21926                 tag: 'th',
21927                 cls: 'prev',
21928                 html: '<i class="fa fa-arrow-left"/>'
21929             },
21930             {
21931                 tag: 'th',
21932                 cls: 'switch',
21933                 colspan: '5'
21934             },
21935             {
21936                 tag: 'th',
21937                 cls: 'next',
21938                 html: '<i class="fa fa-arrow-right"/>'
21939             }
21940
21941             ]
21942         }
21943         ]
21944     },
21945     
21946     content : {
21947         tag: 'tbody',
21948         cn: [
21949         {
21950             tag: 'tr',
21951             cn: [
21952             {
21953                 tag: 'td',
21954                 colspan: '7'
21955             }
21956             ]
21957         }
21958         ]
21959     },
21960     
21961     footer : {
21962         tag: 'tfoot',
21963         cn: [
21964         {
21965             tag: 'tr',
21966             cn: [
21967             {
21968                 tag: 'th',
21969                 colspan: '7',
21970                 cls: 'today'
21971             }
21972                     
21973             ]
21974         }
21975         ]
21976     },
21977     
21978     dates:{
21979         en: {
21980             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21981             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21982             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21983             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21984             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21985             today: "Today"
21986         }
21987     },
21988     
21989     modes: [
21990     {
21991         clsName: 'days',
21992         navFnc: 'Month',
21993         navStep: 1
21994     },
21995     {
21996         clsName: 'months',
21997         navFnc: 'FullYear',
21998         navStep: 1
21999     },
22000     {
22001         clsName: 'years',
22002         navFnc: 'FullYear',
22003         navStep: 10
22004     }]
22005 });
22006
22007 Roo.apply(Roo.bootstrap.DateField,  {
22008   
22009     template : {
22010         tag: 'div',
22011         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22012         cn: [
22013         {
22014             tag: 'div',
22015             cls: 'datepicker-days',
22016             cn: [
22017             {
22018                 tag: 'table',
22019                 cls: 'table-condensed',
22020                 cn:[
22021                 Roo.bootstrap.DateField.head,
22022                 {
22023                     tag: 'tbody'
22024                 },
22025                 Roo.bootstrap.DateField.footer
22026                 ]
22027             }
22028             ]
22029         },
22030         {
22031             tag: 'div',
22032             cls: 'datepicker-months',
22033             cn: [
22034             {
22035                 tag: 'table',
22036                 cls: 'table-condensed',
22037                 cn:[
22038                 Roo.bootstrap.DateField.head,
22039                 Roo.bootstrap.DateField.content,
22040                 Roo.bootstrap.DateField.footer
22041                 ]
22042             }
22043             ]
22044         },
22045         {
22046             tag: 'div',
22047             cls: 'datepicker-years',
22048             cn: [
22049             {
22050                 tag: 'table',
22051                 cls: 'table-condensed',
22052                 cn:[
22053                 Roo.bootstrap.DateField.head,
22054                 Roo.bootstrap.DateField.content,
22055                 Roo.bootstrap.DateField.footer
22056                 ]
22057             }
22058             ]
22059         }
22060         ]
22061     }
22062 });
22063
22064  
22065
22066  /*
22067  * - LGPL
22068  *
22069  * TimeField
22070  * 
22071  */
22072
22073 /**
22074  * @class Roo.bootstrap.TimeField
22075  * @extends Roo.bootstrap.Input
22076  * Bootstrap DateField class
22077  * 
22078  * 
22079  * @constructor
22080  * Create a new TimeField
22081  * @param {Object} config The config object
22082  */
22083
22084 Roo.bootstrap.TimeField = function(config){
22085     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22086     this.addEvents({
22087             /**
22088              * @event show
22089              * Fires when this field show.
22090              * @param {Roo.bootstrap.DateField} thisthis
22091              * @param {Mixed} date The date value
22092              */
22093             show : true,
22094             /**
22095              * @event show
22096              * Fires when this field hide.
22097              * @param {Roo.bootstrap.DateField} this
22098              * @param {Mixed} date The date value
22099              */
22100             hide : true,
22101             /**
22102              * @event select
22103              * Fires when select a date.
22104              * @param {Roo.bootstrap.DateField} this
22105              * @param {Mixed} date The date value
22106              */
22107             select : true
22108         });
22109 };
22110
22111 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22112     
22113     /**
22114      * @cfg {String} format
22115      * The default time format string which can be overriden for localization support.  The format must be
22116      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22117      */
22118     format : "H:i",
22119
22120     getAutoCreate : function()
22121     {
22122         this.after = '<i class="fa far fa-clock"></i>';
22123         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22124         
22125          
22126     },
22127     onRender: function(ct, position)
22128     {
22129         
22130         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22131                 
22132         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22133         
22134         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22135         
22136         this.pop = this.picker().select('>.datepicker-time',true).first();
22137         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22138         
22139         this.picker().on('mousedown', this.onMousedown, this);
22140         this.picker().on('click', this.onClick, this);
22141         
22142         this.picker().addClass('datepicker-dropdown');
22143     
22144         this.fillTime();
22145         this.update();
22146             
22147         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22148         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22149         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22150         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22151         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22152         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22153
22154     },
22155     
22156     fireKey: function(e){
22157         if (!this.picker().isVisible()){
22158             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22159                 this.show();
22160             }
22161             return;
22162         }
22163
22164         e.preventDefault();
22165         
22166         switch(e.keyCode){
22167             case 27: // escape
22168                 this.hide();
22169                 break;
22170             case 37: // left
22171             case 39: // right
22172                 this.onTogglePeriod();
22173                 break;
22174             case 38: // up
22175                 this.onIncrementMinutes();
22176                 break;
22177             case 40: // down
22178                 this.onDecrementMinutes();
22179                 break;
22180             case 13: // enter
22181             case 9: // tab
22182                 this.setTime();
22183                 break;
22184         }
22185     },
22186     
22187     onClick: function(e) {
22188         e.stopPropagation();
22189         e.preventDefault();
22190     },
22191     
22192     picker : function()
22193     {
22194         return this.pickerEl;
22195     },
22196     
22197     fillTime: function()
22198     {    
22199         var time = this.pop.select('tbody', true).first();
22200         
22201         time.dom.innerHTML = '';
22202         
22203         time.createChild({
22204             tag: 'tr',
22205             cn: [
22206                 {
22207                     tag: 'td',
22208                     cn: [
22209                         {
22210                             tag: 'a',
22211                             href: '#',
22212                             cls: 'btn',
22213                             cn: [
22214                                 {
22215                                     tag: 'i',
22216                                     cls: 'hours-up fa fas fa-chevron-up'
22217                                 }
22218                             ]
22219                         } 
22220                     ]
22221                 },
22222                 {
22223                     tag: 'td',
22224                     cls: 'separator'
22225                 },
22226                 {
22227                     tag: 'td',
22228                     cn: [
22229                         {
22230                             tag: 'a',
22231                             href: '#',
22232                             cls: 'btn',
22233                             cn: [
22234                                 {
22235                                     tag: 'i',
22236                                     cls: 'minutes-up fa fas fa-chevron-up'
22237                                 }
22238                             ]
22239                         }
22240                     ]
22241                 },
22242                 {
22243                     tag: 'td',
22244                     cls: 'separator'
22245                 }
22246             ]
22247         });
22248         
22249         time.createChild({
22250             tag: 'tr',
22251             cn: [
22252                 {
22253                     tag: 'td',
22254                     cn: [
22255                         {
22256                             tag: 'span',
22257                             cls: 'timepicker-hour',
22258                             html: '00'
22259                         }  
22260                     ]
22261                 },
22262                 {
22263                     tag: 'td',
22264                     cls: 'separator',
22265                     html: ':'
22266                 },
22267                 {
22268                     tag: 'td',
22269                     cn: [
22270                         {
22271                             tag: 'span',
22272                             cls: 'timepicker-minute',
22273                             html: '00'
22274                         }  
22275                     ]
22276                 },
22277                 {
22278                     tag: 'td',
22279                     cls: 'separator'
22280                 },
22281                 {
22282                     tag: 'td',
22283                     cn: [
22284                         {
22285                             tag: 'button',
22286                             type: 'button',
22287                             cls: 'btn btn-primary period',
22288                             html: 'AM'
22289                             
22290                         }
22291                     ]
22292                 }
22293             ]
22294         });
22295         
22296         time.createChild({
22297             tag: 'tr',
22298             cn: [
22299                 {
22300                     tag: 'td',
22301                     cn: [
22302                         {
22303                             tag: 'a',
22304                             href: '#',
22305                             cls: 'btn',
22306                             cn: [
22307                                 {
22308                                     tag: 'span',
22309                                     cls: 'hours-down fa fas fa-chevron-down'
22310                                 }
22311                             ]
22312                         }
22313                     ]
22314                 },
22315                 {
22316                     tag: 'td',
22317                     cls: 'separator'
22318                 },
22319                 {
22320                     tag: 'td',
22321                     cn: [
22322                         {
22323                             tag: 'a',
22324                             href: '#',
22325                             cls: 'btn',
22326                             cn: [
22327                                 {
22328                                     tag: 'span',
22329                                     cls: 'minutes-down fa fas fa-chevron-down'
22330                                 }
22331                             ]
22332                         }
22333                     ]
22334                 },
22335                 {
22336                     tag: 'td',
22337                     cls: 'separator'
22338                 }
22339             ]
22340         });
22341         
22342     },
22343     
22344     update: function()
22345     {
22346         
22347         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22348         
22349         this.fill();
22350     },
22351     
22352     fill: function() 
22353     {
22354         var hours = this.time.getHours();
22355         var minutes = this.time.getMinutes();
22356         var period = 'AM';
22357         
22358         if(hours > 11){
22359             period = 'PM';
22360         }
22361         
22362         if(hours == 0){
22363             hours = 12;
22364         }
22365         
22366         
22367         if(hours > 12){
22368             hours = hours - 12;
22369         }
22370         
22371         if(hours < 10){
22372             hours = '0' + hours;
22373         }
22374         
22375         if(minutes < 10){
22376             minutes = '0' + minutes;
22377         }
22378         
22379         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22380         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22381         this.pop.select('button', true).first().dom.innerHTML = period;
22382         
22383     },
22384     
22385     place: function()
22386     {   
22387         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22388         
22389         var cls = ['bottom'];
22390         
22391         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22392             cls.pop();
22393             cls.push('top');
22394         }
22395         
22396         cls.push('right');
22397         
22398         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22399             cls.pop();
22400             cls.push('left');
22401         }
22402         //this.picker().setXY(20000,20000);
22403         this.picker().addClass(cls.join('-'));
22404         
22405         var _this = this;
22406         
22407         Roo.each(cls, function(c){
22408             if(c == 'bottom'){
22409                 (function() {
22410                  //  
22411                 }).defer(200);
22412                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22413                 //_this.picker().setTop(_this.inputEl().getHeight());
22414                 return;
22415             }
22416             if(c == 'top'){
22417                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22418                 
22419                 //_this.picker().setTop(0 - _this.picker().getHeight());
22420                 return;
22421             }
22422             /*
22423             if(c == 'left'){
22424                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22425                 return;
22426             }
22427             if(c == 'right'){
22428                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22429                 return;
22430             }
22431             */
22432         });
22433         
22434     },
22435   
22436     onFocus : function()
22437     {
22438         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22439         this.show();
22440     },
22441     
22442     onBlur : function()
22443     {
22444         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22445         this.hide();
22446     },
22447     
22448     show : function()
22449     {
22450         this.picker().show();
22451         this.pop.show();
22452         this.update();
22453         this.place();
22454         
22455         this.fireEvent('show', this, this.date);
22456     },
22457     
22458     hide : function()
22459     {
22460         this.picker().hide();
22461         this.pop.hide();
22462         
22463         this.fireEvent('hide', this, this.date);
22464     },
22465     
22466     setTime : function()
22467     {
22468         this.hide();
22469         this.setValue(this.time.format(this.format));
22470         
22471         this.fireEvent('select', this, this.date);
22472         
22473         
22474     },
22475     
22476     onMousedown: function(e){
22477         e.stopPropagation();
22478         e.preventDefault();
22479     },
22480     
22481     onIncrementHours: function()
22482     {
22483         Roo.log('onIncrementHours');
22484         this.time = this.time.add(Date.HOUR, 1);
22485         this.update();
22486         
22487     },
22488     
22489     onDecrementHours: function()
22490     {
22491         Roo.log('onDecrementHours');
22492         this.time = this.time.add(Date.HOUR, -1);
22493         this.update();
22494     },
22495     
22496     onIncrementMinutes: function()
22497     {
22498         Roo.log('onIncrementMinutes');
22499         this.time = this.time.add(Date.MINUTE, 1);
22500         this.update();
22501     },
22502     
22503     onDecrementMinutes: function()
22504     {
22505         Roo.log('onDecrementMinutes');
22506         this.time = this.time.add(Date.MINUTE, -1);
22507         this.update();
22508     },
22509     
22510     onTogglePeriod: function()
22511     {
22512         Roo.log('onTogglePeriod');
22513         this.time = this.time.add(Date.HOUR, 12);
22514         this.update();
22515     }
22516     
22517    
22518 });
22519  
22520
22521 Roo.apply(Roo.bootstrap.TimeField,  {
22522   
22523     template : {
22524         tag: 'div',
22525         cls: 'datepicker dropdown-menu',
22526         cn: [
22527             {
22528                 tag: 'div',
22529                 cls: 'datepicker-time',
22530                 cn: [
22531                 {
22532                     tag: 'table',
22533                     cls: 'table-condensed',
22534                     cn:[
22535                         {
22536                             tag: 'tbody',
22537                             cn: [
22538                                 {
22539                                     tag: 'tr',
22540                                     cn: [
22541                                     {
22542                                         tag: 'td',
22543                                         colspan: '7'
22544                                     }
22545                                     ]
22546                                 }
22547                             ]
22548                         },
22549                         {
22550                             tag: 'tfoot',
22551                             cn: [
22552                                 {
22553                                     tag: 'tr',
22554                                     cn: [
22555                                     {
22556                                         tag: 'th',
22557                                         colspan: '7',
22558                                         cls: '',
22559                                         cn: [
22560                                             {
22561                                                 tag: 'button',
22562                                                 cls: 'btn btn-info ok',
22563                                                 html: 'OK'
22564                                             }
22565                                         ]
22566                                     }
22567                     
22568                                     ]
22569                                 }
22570                             ]
22571                         }
22572                     ]
22573                 }
22574                 ]
22575             }
22576         ]
22577     }
22578 });
22579
22580  
22581
22582  /*
22583  * - LGPL
22584  *
22585  * MonthField
22586  * 
22587  */
22588
22589 /**
22590  * @class Roo.bootstrap.MonthField
22591  * @extends Roo.bootstrap.Input
22592  * Bootstrap MonthField class
22593  * 
22594  * @cfg {String} language default en
22595  * 
22596  * @constructor
22597  * Create a new MonthField
22598  * @param {Object} config The config object
22599  */
22600
22601 Roo.bootstrap.MonthField = function(config){
22602     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22603     
22604     this.addEvents({
22605         /**
22606          * @event show
22607          * Fires when this field show.
22608          * @param {Roo.bootstrap.MonthField} this
22609          * @param {Mixed} date The date value
22610          */
22611         show : true,
22612         /**
22613          * @event show
22614          * Fires when this field hide.
22615          * @param {Roo.bootstrap.MonthField} this
22616          * @param {Mixed} date The date value
22617          */
22618         hide : true,
22619         /**
22620          * @event select
22621          * Fires when select a date.
22622          * @param {Roo.bootstrap.MonthField} this
22623          * @param {String} oldvalue The old value
22624          * @param {String} newvalue The new value
22625          */
22626         select : true
22627     });
22628 };
22629
22630 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22631     
22632     onRender: function(ct, position)
22633     {
22634         
22635         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22636         
22637         this.language = this.language || 'en';
22638         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22639         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22640         
22641         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22642         this.isInline = false;
22643         this.isInput = true;
22644         this.component = this.el.select('.add-on', true).first() || false;
22645         this.component = (this.component && this.component.length === 0) ? false : this.component;
22646         this.hasInput = this.component && this.inputEL().length;
22647         
22648         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22649         
22650         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22651         
22652         this.picker().on('mousedown', this.onMousedown, this);
22653         this.picker().on('click', this.onClick, this);
22654         
22655         this.picker().addClass('datepicker-dropdown');
22656         
22657         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22658             v.setStyle('width', '189px');
22659         });
22660         
22661         this.fillMonths();
22662         
22663         this.update();
22664         
22665         if(this.isInline) {
22666             this.show();
22667         }
22668         
22669     },
22670     
22671     setValue: function(v, suppressEvent)
22672     {   
22673         var o = this.getValue();
22674         
22675         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22676         
22677         this.update();
22678
22679         if(suppressEvent !== true){
22680             this.fireEvent('select', this, o, v);
22681         }
22682         
22683     },
22684     
22685     getValue: function()
22686     {
22687         return this.value;
22688     },
22689     
22690     onClick: function(e) 
22691     {
22692         e.stopPropagation();
22693         e.preventDefault();
22694         
22695         var target = e.getTarget();
22696         
22697         if(target.nodeName.toLowerCase() === 'i'){
22698             target = Roo.get(target).dom.parentNode;
22699         }
22700         
22701         var nodeName = target.nodeName;
22702         var className = target.className;
22703         var html = target.innerHTML;
22704         
22705         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22706             return;
22707         }
22708         
22709         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22710         
22711         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22712         
22713         this.hide();
22714                         
22715     },
22716     
22717     picker : function()
22718     {
22719         return this.pickerEl;
22720     },
22721     
22722     fillMonths: function()
22723     {    
22724         var i = 0;
22725         var months = this.picker().select('>.datepicker-months td', true).first();
22726         
22727         months.dom.innerHTML = '';
22728         
22729         while (i < 12) {
22730             var month = {
22731                 tag: 'span',
22732                 cls: 'month',
22733                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22734             };
22735             
22736             months.createChild(month);
22737         }
22738         
22739     },
22740     
22741     update: function()
22742     {
22743         var _this = this;
22744         
22745         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22746             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22747         }
22748         
22749         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22750             e.removeClass('active');
22751             
22752             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22753                 e.addClass('active');
22754             }
22755         })
22756     },
22757     
22758     place: function()
22759     {
22760         if(this.isInline) {
22761             return;
22762         }
22763         
22764         this.picker().removeClass(['bottom', 'top']);
22765         
22766         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22767             /*
22768              * place to the top of element!
22769              *
22770              */
22771             
22772             this.picker().addClass('top');
22773             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22774             
22775             return;
22776         }
22777         
22778         this.picker().addClass('bottom');
22779         
22780         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22781     },
22782     
22783     onFocus : function()
22784     {
22785         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22786         this.show();
22787     },
22788     
22789     onBlur : function()
22790     {
22791         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22792         
22793         var d = this.inputEl().getValue();
22794         
22795         this.setValue(d);
22796                 
22797         this.hide();
22798     },
22799     
22800     show : function()
22801     {
22802         this.picker().show();
22803         this.picker().select('>.datepicker-months', true).first().show();
22804         this.update();
22805         this.place();
22806         
22807         this.fireEvent('show', this, this.date);
22808     },
22809     
22810     hide : function()
22811     {
22812         if(this.isInline) {
22813             return;
22814         }
22815         this.picker().hide();
22816         this.fireEvent('hide', this, this.date);
22817         
22818     },
22819     
22820     onMousedown: function(e)
22821     {
22822         e.stopPropagation();
22823         e.preventDefault();
22824     },
22825     
22826     keyup: function(e)
22827     {
22828         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22829         this.update();
22830     },
22831
22832     fireKey: function(e)
22833     {
22834         if (!this.picker().isVisible()){
22835             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22836                 this.show();
22837             }
22838             return;
22839         }
22840         
22841         var dir;
22842         
22843         switch(e.keyCode){
22844             case 27: // escape
22845                 this.hide();
22846                 e.preventDefault();
22847                 break;
22848             case 37: // left
22849             case 39: // right
22850                 dir = e.keyCode == 37 ? -1 : 1;
22851                 
22852                 this.vIndex = this.vIndex + dir;
22853                 
22854                 if(this.vIndex < 0){
22855                     this.vIndex = 0;
22856                 }
22857                 
22858                 if(this.vIndex > 11){
22859                     this.vIndex = 11;
22860                 }
22861                 
22862                 if(isNaN(this.vIndex)){
22863                     this.vIndex = 0;
22864                 }
22865                 
22866                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22867                 
22868                 break;
22869             case 38: // up
22870             case 40: // down
22871                 
22872                 dir = e.keyCode == 38 ? -1 : 1;
22873                 
22874                 this.vIndex = this.vIndex + dir * 4;
22875                 
22876                 if(this.vIndex < 0){
22877                     this.vIndex = 0;
22878                 }
22879                 
22880                 if(this.vIndex > 11){
22881                     this.vIndex = 11;
22882                 }
22883                 
22884                 if(isNaN(this.vIndex)){
22885                     this.vIndex = 0;
22886                 }
22887                 
22888                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22889                 break;
22890                 
22891             case 13: // enter
22892                 
22893                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22894                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22895                 }
22896                 
22897                 this.hide();
22898                 e.preventDefault();
22899                 break;
22900             case 9: // tab
22901                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22902                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22903                 }
22904                 this.hide();
22905                 break;
22906             case 16: // shift
22907             case 17: // ctrl
22908             case 18: // alt
22909                 break;
22910             default :
22911                 this.hide();
22912                 
22913         }
22914     },
22915     
22916     remove: function() 
22917     {
22918         this.picker().remove();
22919     }
22920    
22921 });
22922
22923 Roo.apply(Roo.bootstrap.MonthField,  {
22924     
22925     content : {
22926         tag: 'tbody',
22927         cn: [
22928         {
22929             tag: 'tr',
22930             cn: [
22931             {
22932                 tag: 'td',
22933                 colspan: '7'
22934             }
22935             ]
22936         }
22937         ]
22938     },
22939     
22940     dates:{
22941         en: {
22942             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22943             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22944         }
22945     }
22946 });
22947
22948 Roo.apply(Roo.bootstrap.MonthField,  {
22949   
22950     template : {
22951         tag: 'div',
22952         cls: 'datepicker dropdown-menu roo-dynamic',
22953         cn: [
22954             {
22955                 tag: 'div',
22956                 cls: 'datepicker-months',
22957                 cn: [
22958                 {
22959                     tag: 'table',
22960                     cls: 'table-condensed',
22961                     cn:[
22962                         Roo.bootstrap.DateField.content
22963                     ]
22964                 }
22965                 ]
22966             }
22967         ]
22968     }
22969 });
22970
22971  
22972
22973  
22974  /*
22975  * - LGPL
22976  *
22977  * CheckBox
22978  * 
22979  */
22980
22981 /**
22982  * @class Roo.bootstrap.CheckBox
22983  * @extends Roo.bootstrap.Input
22984  * Bootstrap CheckBox class
22985  * 
22986  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22987  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22988  * @cfg {String} boxLabel The text that appears beside the checkbox
22989  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22990  * @cfg {Boolean} checked initnal the element
22991  * @cfg {Boolean} inline inline the element (default false)
22992  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22993  * @cfg {String} tooltip label tooltip
22994  * 
22995  * @constructor
22996  * Create a new CheckBox
22997  * @param {Object} config The config object
22998  */
22999
23000 Roo.bootstrap.CheckBox = function(config){
23001     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23002    
23003     this.addEvents({
23004         /**
23005         * @event check
23006         * Fires when the element is checked or unchecked.
23007         * @param {Roo.bootstrap.CheckBox} this This input
23008         * @param {Boolean} checked The new checked value
23009         */
23010        check : true,
23011        /**
23012         * @event click
23013         * Fires when the element is click.
23014         * @param {Roo.bootstrap.CheckBox} this This input
23015         */
23016        click : true
23017     });
23018     
23019 };
23020
23021 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23022   
23023     inputType: 'checkbox',
23024     inputValue: 1,
23025     valueOff: 0,
23026     boxLabel: false,
23027     checked: false,
23028     weight : false,
23029     inline: false,
23030     tooltip : '',
23031     
23032     // checkbox success does not make any sense really.. 
23033     invalidClass : "",
23034     validClass : "",
23035     
23036     
23037     getAutoCreate : function()
23038     {
23039         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23040         
23041         var id = Roo.id();
23042         
23043         var cfg = {};
23044         
23045         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23046         
23047         if(this.inline){
23048             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23049         }
23050         
23051         var input =  {
23052             tag: 'input',
23053             id : id,
23054             type : this.inputType,
23055             value : this.inputValue,
23056             cls : 'roo-' + this.inputType, //'form-box',
23057             placeholder : this.placeholder || ''
23058             
23059         };
23060         
23061         if(this.inputType != 'radio'){
23062             var hidden =  {
23063                 tag: 'input',
23064                 type : 'hidden',
23065                 cls : 'roo-hidden-value',
23066                 value : this.checked ? this.inputValue : this.valueOff
23067             };
23068         }
23069         
23070             
23071         if (this.weight) { // Validity check?
23072             cfg.cls += " " + this.inputType + "-" + this.weight;
23073         }
23074         
23075         if (this.disabled) {
23076             input.disabled=true;
23077         }
23078         
23079         if(this.checked){
23080             input.checked = this.checked;
23081         }
23082         
23083         if (this.name) {
23084             
23085             input.name = this.name;
23086             
23087             if(this.inputType != 'radio'){
23088                 hidden.name = this.name;
23089                 input.name = '_hidden_' + this.name;
23090             }
23091         }
23092         
23093         if (this.size) {
23094             input.cls += ' input-' + this.size;
23095         }
23096         
23097         var settings=this;
23098         
23099         ['xs','sm','md','lg'].map(function(size){
23100             if (settings[size]) {
23101                 cfg.cls += ' col-' + size + '-' + settings[size];
23102             }
23103         });
23104         
23105         var inputblock = input;
23106          
23107         if (this.before || this.after) {
23108             
23109             inputblock = {
23110                 cls : 'input-group',
23111                 cn :  [] 
23112             };
23113             
23114             if (this.before) {
23115                 inputblock.cn.push({
23116                     tag :'span',
23117                     cls : 'input-group-addon',
23118                     html : this.before
23119                 });
23120             }
23121             
23122             inputblock.cn.push(input);
23123             
23124             if(this.inputType != 'radio'){
23125                 inputblock.cn.push(hidden);
23126             }
23127             
23128             if (this.after) {
23129                 inputblock.cn.push({
23130                     tag :'span',
23131                     cls : 'input-group-addon',
23132                     html : this.after
23133                 });
23134             }
23135             
23136         }
23137         var boxLabelCfg = false;
23138         
23139         if(this.boxLabel){
23140            
23141             boxLabelCfg = {
23142                 tag: 'label',
23143                 //'for': id, // box label is handled by onclick - so no for...
23144                 cls: 'box-label',
23145                 html: this.boxLabel
23146             };
23147             if(this.tooltip){
23148                 boxLabelCfg.tooltip = this.tooltip;
23149             }
23150              
23151         }
23152         
23153         
23154         if (align ==='left' && this.fieldLabel.length) {
23155 //                Roo.log("left and has label");
23156             cfg.cn = [
23157                 {
23158                     tag: 'label',
23159                     'for' :  id,
23160                     cls : 'control-label',
23161                     html : this.fieldLabel
23162                 },
23163                 {
23164                     cls : "", 
23165                     cn: [
23166                         inputblock
23167                     ]
23168                 }
23169             ];
23170             
23171             if (boxLabelCfg) {
23172                 cfg.cn[1].cn.push(boxLabelCfg);
23173             }
23174             
23175             if(this.labelWidth > 12){
23176                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23177             }
23178             
23179             if(this.labelWidth < 13 && this.labelmd == 0){
23180                 this.labelmd = this.labelWidth;
23181             }
23182             
23183             if(this.labellg > 0){
23184                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23185                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23186             }
23187             
23188             if(this.labelmd > 0){
23189                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23190                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23191             }
23192             
23193             if(this.labelsm > 0){
23194                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23195                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23196             }
23197             
23198             if(this.labelxs > 0){
23199                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23200                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23201             }
23202             
23203         } else if ( this.fieldLabel.length) {
23204 //                Roo.log(" label");
23205                 cfg.cn = [
23206                    
23207                     {
23208                         tag: this.boxLabel ? 'span' : 'label',
23209                         'for': id,
23210                         cls: 'control-label box-input-label',
23211                         //cls : 'input-group-addon',
23212                         html : this.fieldLabel
23213                     },
23214                     
23215                     inputblock
23216                     
23217                 ];
23218                 if (boxLabelCfg) {
23219                     cfg.cn.push(boxLabelCfg);
23220                 }
23221
23222         } else {
23223             
23224 //                Roo.log(" no label && no align");
23225                 cfg.cn = [  inputblock ] ;
23226                 if (boxLabelCfg) {
23227                     cfg.cn.push(boxLabelCfg);
23228                 }
23229
23230                 
23231         }
23232         
23233        
23234         
23235         if(this.inputType != 'radio'){
23236             cfg.cn.push(hidden);
23237         }
23238         
23239         return cfg;
23240         
23241     },
23242     
23243     /**
23244      * return the real input element.
23245      */
23246     inputEl: function ()
23247     {
23248         return this.el.select('input.roo-' + this.inputType,true).first();
23249     },
23250     hiddenEl: function ()
23251     {
23252         return this.el.select('input.roo-hidden-value',true).first();
23253     },
23254     
23255     labelEl: function()
23256     {
23257         return this.el.select('label.control-label',true).first();
23258     },
23259     /* depricated... */
23260     
23261     label: function()
23262     {
23263         return this.labelEl();
23264     },
23265     
23266     boxLabelEl: function()
23267     {
23268         return this.el.select('label.box-label',true).first();
23269     },
23270     
23271     initEvents : function()
23272     {
23273 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23274         
23275         this.inputEl().on('click', this.onClick,  this);
23276         
23277         if (this.boxLabel) { 
23278             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23279         }
23280         
23281         this.startValue = this.getValue();
23282         
23283         if(this.groupId){
23284             Roo.bootstrap.CheckBox.register(this);
23285         }
23286     },
23287     
23288     onClick : function(e)
23289     {   
23290         if(this.fireEvent('click', this, e) !== false){
23291             this.setChecked(!this.checked);
23292         }
23293         
23294     },
23295     
23296     setChecked : function(state,suppressEvent)
23297     {
23298         this.startValue = this.getValue();
23299
23300         if(this.inputType == 'radio'){
23301             
23302             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23303                 e.dom.checked = false;
23304             });
23305             
23306             this.inputEl().dom.checked = true;
23307             
23308             this.inputEl().dom.value = this.inputValue;
23309             
23310             if(suppressEvent !== true){
23311                 this.fireEvent('check', this, true);
23312             }
23313             
23314             this.validate();
23315             
23316             return;
23317         }
23318         
23319         this.checked = state;
23320         
23321         this.inputEl().dom.checked = state;
23322         
23323         
23324         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23325         
23326         if(suppressEvent !== true){
23327             this.fireEvent('check', this, state);
23328         }
23329         
23330         this.validate();
23331     },
23332     
23333     getValue : function()
23334     {
23335         if(this.inputType == 'radio'){
23336             return this.getGroupValue();
23337         }
23338         
23339         return this.hiddenEl().dom.value;
23340         
23341     },
23342     
23343     getGroupValue : function()
23344     {
23345         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23346             return '';
23347         }
23348         
23349         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23350     },
23351     
23352     setValue : function(v,suppressEvent)
23353     {
23354         if(this.inputType == 'radio'){
23355             this.setGroupValue(v, suppressEvent);
23356             return;
23357         }
23358         
23359         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23360         
23361         this.validate();
23362     },
23363     
23364     setGroupValue : function(v, suppressEvent)
23365     {
23366         this.startValue = this.getValue();
23367         
23368         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23369             e.dom.checked = false;
23370             
23371             if(e.dom.value == v){
23372                 e.dom.checked = true;
23373             }
23374         });
23375         
23376         if(suppressEvent !== true){
23377             this.fireEvent('check', this, true);
23378         }
23379
23380         this.validate();
23381         
23382         return;
23383     },
23384     
23385     validate : function()
23386     {
23387         if(this.getVisibilityEl().hasClass('hidden')){
23388             return true;
23389         }
23390         
23391         if(
23392                 this.disabled || 
23393                 (this.inputType == 'radio' && this.validateRadio()) ||
23394                 (this.inputType == 'checkbox' && this.validateCheckbox())
23395         ){
23396             this.markValid();
23397             return true;
23398         }
23399         
23400         this.markInvalid();
23401         return false;
23402     },
23403     
23404     validateRadio : function()
23405     {
23406         if(this.getVisibilityEl().hasClass('hidden')){
23407             return true;
23408         }
23409         
23410         if(this.allowBlank){
23411             return true;
23412         }
23413         
23414         var valid = false;
23415         
23416         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23417             if(!e.dom.checked){
23418                 return;
23419             }
23420             
23421             valid = true;
23422             
23423             return false;
23424         });
23425         
23426         return valid;
23427     },
23428     
23429     validateCheckbox : function()
23430     {
23431         if(!this.groupId){
23432             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23433             //return (this.getValue() == this.inputValue) ? true : false;
23434         }
23435         
23436         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23437         
23438         if(!group){
23439             return false;
23440         }
23441         
23442         var r = false;
23443         
23444         for(var i in group){
23445             if(group[i].el.isVisible(true)){
23446                 r = false;
23447                 break;
23448             }
23449             
23450             r = true;
23451         }
23452         
23453         for(var i in group){
23454             if(r){
23455                 break;
23456             }
23457             
23458             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23459         }
23460         
23461         return r;
23462     },
23463     
23464     /**
23465      * Mark this field as valid
23466      */
23467     markValid : function()
23468     {
23469         var _this = this;
23470         
23471         this.fireEvent('valid', this);
23472         
23473         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23474         
23475         if(this.groupId){
23476             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23477         }
23478         
23479         if(label){
23480             label.markValid();
23481         }
23482
23483         if(this.inputType == 'radio'){
23484             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23485                 var fg = e.findParent('.form-group', false, true);
23486                 if (Roo.bootstrap.version == 3) {
23487                     fg.removeClass([_this.invalidClass, _this.validClass]);
23488                     fg.addClass(_this.validClass);
23489                 } else {
23490                     fg.removeClass(['is-valid', 'is-invalid']);
23491                     fg.addClass('is-valid');
23492                 }
23493             });
23494             
23495             return;
23496         }
23497
23498         if(!this.groupId){
23499             var fg = this.el.findParent('.form-group', false, true);
23500             if (Roo.bootstrap.version == 3) {
23501                 fg.removeClass([this.invalidClass, this.validClass]);
23502                 fg.addClass(this.validClass);
23503             } else {
23504                 fg.removeClass(['is-valid', 'is-invalid']);
23505                 fg.addClass('is-valid');
23506             }
23507             return;
23508         }
23509         
23510         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23511         
23512         if(!group){
23513             return;
23514         }
23515         
23516         for(var i in group){
23517             var fg = group[i].el.findParent('.form-group', false, true);
23518             if (Roo.bootstrap.version == 3) {
23519                 fg.removeClass([this.invalidClass, this.validClass]);
23520                 fg.addClass(this.validClass);
23521             } else {
23522                 fg.removeClass(['is-valid', 'is-invalid']);
23523                 fg.addClass('is-valid');
23524             }
23525         }
23526     },
23527     
23528      /**
23529      * Mark this field as invalid
23530      * @param {String} msg The validation message
23531      */
23532     markInvalid : function(msg)
23533     {
23534         if(this.allowBlank){
23535             return;
23536         }
23537         
23538         var _this = this;
23539         
23540         this.fireEvent('invalid', this, msg);
23541         
23542         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23543         
23544         if(this.groupId){
23545             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23546         }
23547         
23548         if(label){
23549             label.markInvalid();
23550         }
23551             
23552         if(this.inputType == 'radio'){
23553             
23554             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23555                 var fg = e.findParent('.form-group', false, true);
23556                 if (Roo.bootstrap.version == 3) {
23557                     fg.removeClass([_this.invalidClass, _this.validClass]);
23558                     fg.addClass(_this.invalidClass);
23559                 } else {
23560                     fg.removeClass(['is-invalid', 'is-valid']);
23561                     fg.addClass('is-invalid');
23562                 }
23563             });
23564             
23565             return;
23566         }
23567         
23568         if(!this.groupId){
23569             var fg = this.el.findParent('.form-group', false, true);
23570             if (Roo.bootstrap.version == 3) {
23571                 fg.removeClass([_this.invalidClass, _this.validClass]);
23572                 fg.addClass(_this.invalidClass);
23573             } else {
23574                 fg.removeClass(['is-invalid', 'is-valid']);
23575                 fg.addClass('is-invalid');
23576             }
23577             return;
23578         }
23579         
23580         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23581         
23582         if(!group){
23583             return;
23584         }
23585         
23586         for(var i in group){
23587             var fg = group[i].el.findParent('.form-group', false, true);
23588             if (Roo.bootstrap.version == 3) {
23589                 fg.removeClass([_this.invalidClass, _this.validClass]);
23590                 fg.addClass(_this.invalidClass);
23591             } else {
23592                 fg.removeClass(['is-invalid', 'is-valid']);
23593                 fg.addClass('is-invalid');
23594             }
23595         }
23596         
23597     },
23598     
23599     clearInvalid : function()
23600     {
23601         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23602         
23603         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23604         
23605         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23606         
23607         if (label && label.iconEl) {
23608             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23609             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23610         }
23611     },
23612     
23613     disable : function()
23614     {
23615         if(this.inputType != 'radio'){
23616             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23617             return;
23618         }
23619         
23620         var _this = this;
23621         
23622         if(this.rendered){
23623             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23624                 _this.getActionEl().addClass(this.disabledClass);
23625                 e.dom.disabled = true;
23626             });
23627         }
23628         
23629         this.disabled = true;
23630         this.fireEvent("disable", this);
23631         return this;
23632     },
23633
23634     enable : function()
23635     {
23636         if(this.inputType != 'radio'){
23637             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23638             return;
23639         }
23640         
23641         var _this = this;
23642         
23643         if(this.rendered){
23644             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23645                 _this.getActionEl().removeClass(this.disabledClass);
23646                 e.dom.disabled = false;
23647             });
23648         }
23649         
23650         this.disabled = false;
23651         this.fireEvent("enable", this);
23652         return this;
23653     },
23654     
23655     setBoxLabel : function(v)
23656     {
23657         this.boxLabel = v;
23658         
23659         if(this.rendered){
23660             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23661         }
23662     }
23663
23664 });
23665
23666 Roo.apply(Roo.bootstrap.CheckBox, {
23667     
23668     groups: {},
23669     
23670      /**
23671     * register a CheckBox Group
23672     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23673     */
23674     register : function(checkbox)
23675     {
23676         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23677             this.groups[checkbox.groupId] = {};
23678         }
23679         
23680         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23681             return;
23682         }
23683         
23684         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23685         
23686     },
23687     /**
23688     * fetch a CheckBox Group based on the group ID
23689     * @param {string} the group ID
23690     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23691     */
23692     get: function(groupId) {
23693         if (typeof(this.groups[groupId]) == 'undefined') {
23694             return false;
23695         }
23696         
23697         return this.groups[groupId] ;
23698     }
23699     
23700     
23701 });
23702 /*
23703  * - LGPL
23704  *
23705  * RadioItem
23706  * 
23707  */
23708
23709 /**
23710  * @class Roo.bootstrap.Radio
23711  * @extends Roo.bootstrap.Component
23712  * Bootstrap Radio class
23713  * @cfg {String} boxLabel - the label associated
23714  * @cfg {String} value - the value of radio
23715  * 
23716  * @constructor
23717  * Create a new Radio
23718  * @param {Object} config The config object
23719  */
23720 Roo.bootstrap.Radio = function(config){
23721     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23722     
23723 };
23724
23725 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23726     
23727     boxLabel : '',
23728     
23729     value : '',
23730     
23731     getAutoCreate : function()
23732     {
23733         var cfg = {
23734             tag : 'div',
23735             cls : 'form-group radio',
23736             cn : [
23737                 {
23738                     tag : 'label',
23739                     cls : 'box-label',
23740                     html : this.boxLabel
23741                 }
23742             ]
23743         };
23744         
23745         return cfg;
23746     },
23747     
23748     initEvents : function() 
23749     {
23750         this.parent().register(this);
23751         
23752         this.el.on('click', this.onClick, this);
23753         
23754     },
23755     
23756     onClick : function(e)
23757     {
23758         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23759             this.setChecked(true);
23760         }
23761     },
23762     
23763     setChecked : function(state, suppressEvent)
23764     {
23765         this.parent().setValue(this.value, suppressEvent);
23766         
23767     },
23768     
23769     setBoxLabel : function(v)
23770     {
23771         this.boxLabel = v;
23772         
23773         if(this.rendered){
23774             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23775         }
23776     }
23777     
23778 });
23779  
23780
23781  /*
23782  * - LGPL
23783  *
23784  * Input
23785  * 
23786  */
23787
23788 /**
23789  * @class Roo.bootstrap.SecurePass
23790  * @extends Roo.bootstrap.Input
23791  * Bootstrap SecurePass class
23792  *
23793  * 
23794  * @constructor
23795  * Create a new SecurePass
23796  * @param {Object} config The config object
23797  */
23798  
23799 Roo.bootstrap.SecurePass = function (config) {
23800     // these go here, so the translation tool can replace them..
23801     this.errors = {
23802         PwdEmpty: "Please type a password, and then retype it to confirm.",
23803         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23804         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23805         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23806         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23807         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23808         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23809         TooWeak: "Your password is Too Weak."
23810     },
23811     this.meterLabel = "Password strength:";
23812     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23813     this.meterClass = [
23814         "roo-password-meter-tooweak", 
23815         "roo-password-meter-weak", 
23816         "roo-password-meter-medium", 
23817         "roo-password-meter-strong", 
23818         "roo-password-meter-grey"
23819     ];
23820     
23821     this.errors = {};
23822     
23823     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23824 }
23825
23826 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23827     /**
23828      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23829      * {
23830      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23831      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23832      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23833      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23834      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23835      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23836      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23837      * })
23838      */
23839     // private
23840     
23841     meterWidth: 300,
23842     errorMsg :'',    
23843     errors: false,
23844     imageRoot: '/',
23845     /**
23846      * @cfg {String/Object} Label for the strength meter (defaults to
23847      * 'Password strength:')
23848      */
23849     // private
23850     meterLabel: '',
23851     /**
23852      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23853      * ['Weak', 'Medium', 'Strong'])
23854      */
23855     // private    
23856     pwdStrengths: false,    
23857     // private
23858     strength: 0,
23859     // private
23860     _lastPwd: null,
23861     // private
23862     kCapitalLetter: 0,
23863     kSmallLetter: 1,
23864     kDigit: 2,
23865     kPunctuation: 3,
23866     
23867     insecure: false,
23868     // private
23869     initEvents: function ()
23870     {
23871         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23872
23873         if (this.el.is('input[type=password]') && Roo.isSafari) {
23874             this.el.on('keydown', this.SafariOnKeyDown, this);
23875         }
23876
23877         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23878     },
23879     // private
23880     onRender: function (ct, position)
23881     {
23882         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23883         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23884         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23885
23886         this.trigger.createChild({
23887                    cn: [
23888                     {
23889                     //id: 'PwdMeter',
23890                     tag: 'div',
23891                     cls: 'roo-password-meter-grey col-xs-12',
23892                     style: {
23893                         //width: 0,
23894                         //width: this.meterWidth + 'px'                                                
23895                         }
23896                     },
23897                     {                            
23898                          cls: 'roo-password-meter-text'                          
23899                     }
23900                 ]            
23901         });
23902
23903          
23904         if (this.hideTrigger) {
23905             this.trigger.setDisplayed(false);
23906         }
23907         this.setSize(this.width || '', this.height || '');
23908     },
23909     // private
23910     onDestroy: function ()
23911     {
23912         if (this.trigger) {
23913             this.trigger.removeAllListeners();
23914             this.trigger.remove();
23915         }
23916         if (this.wrap) {
23917             this.wrap.remove();
23918         }
23919         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23920     },
23921     // private
23922     checkStrength: function ()
23923     {
23924         var pwd = this.inputEl().getValue();
23925         if (pwd == this._lastPwd) {
23926             return;
23927         }
23928
23929         var strength;
23930         if (this.ClientSideStrongPassword(pwd)) {
23931             strength = 3;
23932         } else if (this.ClientSideMediumPassword(pwd)) {
23933             strength = 2;
23934         } else if (this.ClientSideWeakPassword(pwd)) {
23935             strength = 1;
23936         } else {
23937             strength = 0;
23938         }
23939         
23940         Roo.log('strength1: ' + strength);
23941         
23942         //var pm = this.trigger.child('div/div/div').dom;
23943         var pm = this.trigger.child('div/div');
23944         pm.removeClass(this.meterClass);
23945         pm.addClass(this.meterClass[strength]);
23946                 
23947         
23948         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23949                 
23950         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23951         
23952         this._lastPwd = pwd;
23953     },
23954     reset: function ()
23955     {
23956         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23957         
23958         this._lastPwd = '';
23959         
23960         var pm = this.trigger.child('div/div');
23961         pm.removeClass(this.meterClass);
23962         pm.addClass('roo-password-meter-grey');        
23963         
23964         
23965         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23966         
23967         pt.innerHTML = '';
23968         this.inputEl().dom.type='password';
23969     },
23970     // private
23971     validateValue: function (value)
23972     {
23973         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23974             return false;
23975         }
23976         if (value.length == 0) {
23977             if (this.allowBlank) {
23978                 this.clearInvalid();
23979                 return true;
23980             }
23981
23982             this.markInvalid(this.errors.PwdEmpty);
23983             this.errorMsg = this.errors.PwdEmpty;
23984             return false;
23985         }
23986         
23987         if(this.insecure){
23988             return true;
23989         }
23990         
23991         if (!value.match(/[\x21-\x7e]+/)) {
23992             this.markInvalid(this.errors.PwdBadChar);
23993             this.errorMsg = this.errors.PwdBadChar;
23994             return false;
23995         }
23996         if (value.length < 6) {
23997             this.markInvalid(this.errors.PwdShort);
23998             this.errorMsg = this.errors.PwdShort;
23999             return false;
24000         }
24001         if (value.length > 16) {
24002             this.markInvalid(this.errors.PwdLong);
24003             this.errorMsg = this.errors.PwdLong;
24004             return false;
24005         }
24006         var strength;
24007         if (this.ClientSideStrongPassword(value)) {
24008             strength = 3;
24009         } else if (this.ClientSideMediumPassword(value)) {
24010             strength = 2;
24011         } else if (this.ClientSideWeakPassword(value)) {
24012             strength = 1;
24013         } else {
24014             strength = 0;
24015         }
24016
24017         
24018         if (strength < 2) {
24019             //this.markInvalid(this.errors.TooWeak);
24020             this.errorMsg = this.errors.TooWeak;
24021             //return false;
24022         }
24023         
24024         
24025         console.log('strength2: ' + strength);
24026         
24027         //var pm = this.trigger.child('div/div/div').dom;
24028         
24029         var pm = this.trigger.child('div/div');
24030         pm.removeClass(this.meterClass);
24031         pm.addClass(this.meterClass[strength]);
24032                 
24033         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24034                 
24035         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24036         
24037         this.errorMsg = ''; 
24038         return true;
24039     },
24040     // private
24041     CharacterSetChecks: function (type)
24042     {
24043         this.type = type;
24044         this.fResult = false;
24045     },
24046     // private
24047     isctype: function (character, type)
24048     {
24049         switch (type) {  
24050             case this.kCapitalLetter:
24051                 if (character >= 'A' && character <= 'Z') {
24052                     return true;
24053                 }
24054                 break;
24055             
24056             case this.kSmallLetter:
24057                 if (character >= 'a' && character <= 'z') {
24058                     return true;
24059                 }
24060                 break;
24061             
24062             case this.kDigit:
24063                 if (character >= '0' && character <= '9') {
24064                     return true;
24065                 }
24066                 break;
24067             
24068             case this.kPunctuation:
24069                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24070                     return true;
24071                 }
24072                 break;
24073             
24074             default:
24075                 return false;
24076         }
24077
24078     },
24079     // private
24080     IsLongEnough: function (pwd, size)
24081     {
24082         return !(pwd == null || isNaN(size) || pwd.length < size);
24083     },
24084     // private
24085     SpansEnoughCharacterSets: function (word, nb)
24086     {
24087         if (!this.IsLongEnough(word, nb))
24088         {
24089             return false;
24090         }
24091
24092         var characterSetChecks = new Array(
24093             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24094             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24095         );
24096         
24097         for (var index = 0; index < word.length; ++index) {
24098             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24099                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24100                     characterSetChecks[nCharSet].fResult = true;
24101                     break;
24102                 }
24103             }
24104         }
24105
24106         var nCharSets = 0;
24107         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24108             if (characterSetChecks[nCharSet].fResult) {
24109                 ++nCharSets;
24110             }
24111         }
24112
24113         if (nCharSets < nb) {
24114             return false;
24115         }
24116         return true;
24117     },
24118     // private
24119     ClientSideStrongPassword: function (pwd)
24120     {
24121         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24122     },
24123     // private
24124     ClientSideMediumPassword: function (pwd)
24125     {
24126         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24127     },
24128     // private
24129     ClientSideWeakPassword: function (pwd)
24130     {
24131         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24132     }
24133           
24134 })//<script type="text/javascript">
24135
24136 /*
24137  * Based  Ext JS Library 1.1.1
24138  * Copyright(c) 2006-2007, Ext JS, LLC.
24139  * LGPL
24140  *
24141  */
24142  
24143 /**
24144  * @class Roo.HtmlEditorCore
24145  * @extends Roo.Component
24146  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24147  *
24148  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24149  */
24150
24151 Roo.HtmlEditorCore = function(config){
24152     
24153     
24154     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24155     
24156     
24157     this.addEvents({
24158         /**
24159          * @event initialize
24160          * Fires when the editor is fully initialized (including the iframe)
24161          * @param {Roo.HtmlEditorCore} this
24162          */
24163         initialize: true,
24164         /**
24165          * @event activate
24166          * Fires when the editor is first receives the focus. Any insertion must wait
24167          * until after this event.
24168          * @param {Roo.HtmlEditorCore} this
24169          */
24170         activate: true,
24171          /**
24172          * @event beforesync
24173          * Fires before the textarea is updated with content from the editor iframe. Return false
24174          * to cancel the sync.
24175          * @param {Roo.HtmlEditorCore} this
24176          * @param {String} html
24177          */
24178         beforesync: true,
24179          /**
24180          * @event beforepush
24181          * Fires before the iframe editor is updated with content from the textarea. Return false
24182          * to cancel the push.
24183          * @param {Roo.HtmlEditorCore} this
24184          * @param {String} html
24185          */
24186         beforepush: true,
24187          /**
24188          * @event sync
24189          * Fires when the textarea is updated with content from the editor iframe.
24190          * @param {Roo.HtmlEditorCore} this
24191          * @param {String} html
24192          */
24193         sync: true,
24194          /**
24195          * @event push
24196          * Fires when the iframe editor is updated with content from the textarea.
24197          * @param {Roo.HtmlEditorCore} this
24198          * @param {String} html
24199          */
24200         push: true,
24201         
24202         /**
24203          * @event editorevent
24204          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24205          * @param {Roo.HtmlEditorCore} this
24206          */
24207         editorevent: true
24208         
24209     });
24210     
24211     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24212     
24213     // defaults : white / black...
24214     this.applyBlacklists();
24215     
24216     
24217     
24218 };
24219
24220
24221 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24222
24223
24224      /**
24225      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24226      */
24227     
24228     owner : false,
24229     
24230      /**
24231      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24232      *                        Roo.resizable.
24233      */
24234     resizable : false,
24235      /**
24236      * @cfg {Number} height (in pixels)
24237      */   
24238     height: 300,
24239    /**
24240      * @cfg {Number} width (in pixels)
24241      */   
24242     width: 500,
24243     
24244     /**
24245      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24246      * 
24247      */
24248     stylesheets: false,
24249     
24250     // id of frame..
24251     frameId: false,
24252     
24253     // private properties
24254     validationEvent : false,
24255     deferHeight: true,
24256     initialized : false,
24257     activated : false,
24258     sourceEditMode : false,
24259     onFocus : Roo.emptyFn,
24260     iframePad:3,
24261     hideMode:'offsets',
24262     
24263     clearUp: true,
24264     
24265     // blacklist + whitelisted elements..
24266     black: false,
24267     white: false,
24268      
24269     bodyCls : '',
24270
24271     /**
24272      * Protected method that will not generally be called directly. It
24273      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24274      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24275      */
24276     getDocMarkup : function(){
24277         // body styles..
24278         var st = '';
24279         
24280         // inherit styels from page...?? 
24281         if (this.stylesheets === false) {
24282             
24283             Roo.get(document.head).select('style').each(function(node) {
24284                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24285             });
24286             
24287             Roo.get(document.head).select('link').each(function(node) { 
24288                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24289             });
24290             
24291         } else if (!this.stylesheets.length) {
24292                 // simple..
24293                 st = '<style type="text/css">' +
24294                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24295                    '</style>';
24296         } else {
24297             for (var i in this.stylesheets) { 
24298                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24299             }
24300             
24301         }
24302         
24303         st +=  '<style type="text/css">' +
24304             'IMG { cursor: pointer } ' +
24305         '</style>';
24306
24307         var cls = 'roo-htmleditor-body';
24308         
24309         if(this.bodyCls.length){
24310             cls += ' ' + this.bodyCls;
24311         }
24312         
24313         return '<html><head>' + st  +
24314             //<style type="text/css">' +
24315             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24316             //'</style>' +
24317             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24318     },
24319
24320     // private
24321     onRender : function(ct, position)
24322     {
24323         var _t = this;
24324         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24325         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24326         
24327         
24328         this.el.dom.style.border = '0 none';
24329         this.el.dom.setAttribute('tabIndex', -1);
24330         this.el.addClass('x-hidden hide');
24331         
24332         
24333         
24334         if(Roo.isIE){ // fix IE 1px bogus margin
24335             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24336         }
24337        
24338         
24339         this.frameId = Roo.id();
24340         
24341          
24342         
24343         var iframe = this.owner.wrap.createChild({
24344             tag: 'iframe',
24345             cls: 'form-control', // bootstrap..
24346             id: this.frameId,
24347             name: this.frameId,
24348             frameBorder : 'no',
24349             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24350         }, this.el
24351         );
24352         
24353         
24354         this.iframe = iframe.dom;
24355
24356          this.assignDocWin();
24357         
24358         this.doc.designMode = 'on';
24359        
24360         this.doc.open();
24361         this.doc.write(this.getDocMarkup());
24362         this.doc.close();
24363
24364         
24365         var task = { // must defer to wait for browser to be ready
24366             run : function(){
24367                 //console.log("run task?" + this.doc.readyState);
24368                 this.assignDocWin();
24369                 if(this.doc.body || this.doc.readyState == 'complete'){
24370                     try {
24371                         this.doc.designMode="on";
24372                     } catch (e) {
24373                         return;
24374                     }
24375                     Roo.TaskMgr.stop(task);
24376                     this.initEditor.defer(10, this);
24377                 }
24378             },
24379             interval : 10,
24380             duration: 10000,
24381             scope: this
24382         };
24383         Roo.TaskMgr.start(task);
24384
24385     },
24386
24387     // private
24388     onResize : function(w, h)
24389     {
24390          Roo.log('resize: ' +w + ',' + h );
24391         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24392         if(!this.iframe){
24393             return;
24394         }
24395         if(typeof w == 'number'){
24396             
24397             this.iframe.style.width = w + 'px';
24398         }
24399         if(typeof h == 'number'){
24400             
24401             this.iframe.style.height = h + 'px';
24402             if(this.doc){
24403                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24404             }
24405         }
24406         
24407     },
24408
24409     /**
24410      * Toggles the editor between standard and source edit mode.
24411      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24412      */
24413     toggleSourceEdit : function(sourceEditMode){
24414         
24415         this.sourceEditMode = sourceEditMode === true;
24416         
24417         if(this.sourceEditMode){
24418  
24419             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24420             
24421         }else{
24422             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24423             //this.iframe.className = '';
24424             this.deferFocus();
24425         }
24426         //this.setSize(this.owner.wrap.getSize());
24427         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24428     },
24429
24430     
24431   
24432
24433     /**
24434      * Protected method that will not generally be called directly. If you need/want
24435      * custom HTML cleanup, this is the method you should override.
24436      * @param {String} html The HTML to be cleaned
24437      * return {String} The cleaned HTML
24438      */
24439     cleanHtml : function(html){
24440         html = String(html);
24441         if(html.length > 5){
24442             if(Roo.isSafari){ // strip safari nonsense
24443                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24444             }
24445         }
24446         if(html == '&nbsp;'){
24447             html = '';
24448         }
24449         return html;
24450     },
24451
24452     /**
24453      * HTML Editor -> Textarea
24454      * Protected method that will not generally be called directly. Syncs the contents
24455      * of the editor iframe with the textarea.
24456      */
24457     syncValue : function(){
24458         if(this.initialized){
24459             var bd = (this.doc.body || this.doc.documentElement);
24460             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24461             var html = bd.innerHTML;
24462             if(Roo.isSafari){
24463                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24464                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24465                 if(m && m[1]){
24466                     html = '<div style="'+m[0]+'">' + html + '</div>';
24467                 }
24468             }
24469             html = this.cleanHtml(html);
24470             // fix up the special chars.. normaly like back quotes in word...
24471             // however we do not want to do this with chinese..
24472             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24473                 
24474                 var cc = match.charCodeAt();
24475
24476                 // Get the character value, handling surrogate pairs
24477                 if (match.length == 2) {
24478                     // It's a surrogate pair, calculate the Unicode code point
24479                     var high = match.charCodeAt(0) - 0xD800;
24480                     var low  = match.charCodeAt(1) - 0xDC00;
24481                     cc = (high * 0x400) + low + 0x10000;
24482                 }  else if (
24483                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24484                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24485                     (cc >= 0xf900 && cc < 0xfb00 )
24486                 ) {
24487                         return match;
24488                 }  
24489          
24490                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24491                 return "&#" + cc + ";";
24492                 
24493                 
24494             });
24495             
24496             
24497              
24498             if(this.owner.fireEvent('beforesync', this, html) !== false){
24499                 this.el.dom.value = html;
24500                 this.owner.fireEvent('sync', this, html);
24501             }
24502         }
24503     },
24504
24505     /**
24506      * Protected method that will not generally be called directly. Pushes the value of the textarea
24507      * into the iframe editor.
24508      */
24509     pushValue : function(){
24510         if(this.initialized){
24511             var v = this.el.dom.value.trim();
24512             
24513 //            if(v.length < 1){
24514 //                v = '&#160;';
24515 //            }
24516             
24517             if(this.owner.fireEvent('beforepush', this, v) !== false){
24518                 var d = (this.doc.body || this.doc.documentElement);
24519                 d.innerHTML = v;
24520                 this.cleanUpPaste();
24521                 this.el.dom.value = d.innerHTML;
24522                 this.owner.fireEvent('push', this, v);
24523             }
24524         }
24525     },
24526
24527     // private
24528     deferFocus : function(){
24529         this.focus.defer(10, this);
24530     },
24531
24532     // doc'ed in Field
24533     focus : function(){
24534         if(this.win && !this.sourceEditMode){
24535             this.win.focus();
24536         }else{
24537             this.el.focus();
24538         }
24539     },
24540     
24541     assignDocWin: function()
24542     {
24543         var iframe = this.iframe;
24544         
24545          if(Roo.isIE){
24546             this.doc = iframe.contentWindow.document;
24547             this.win = iframe.contentWindow;
24548         } else {
24549 //            if (!Roo.get(this.frameId)) {
24550 //                return;
24551 //            }
24552 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24553 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24554             
24555             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24556                 return;
24557             }
24558             
24559             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24560             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24561         }
24562     },
24563     
24564     // private
24565     initEditor : function(){
24566         //console.log("INIT EDITOR");
24567         this.assignDocWin();
24568         
24569         
24570         
24571         this.doc.designMode="on";
24572         this.doc.open();
24573         this.doc.write(this.getDocMarkup());
24574         this.doc.close();
24575         
24576         var dbody = (this.doc.body || this.doc.documentElement);
24577         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24578         // this copies styles from the containing element into thsi one..
24579         // not sure why we need all of this..
24580         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24581         
24582         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24583         //ss['background-attachment'] = 'fixed'; // w3c
24584         dbody.bgProperties = 'fixed'; // ie
24585         //Roo.DomHelper.applyStyles(dbody, ss);
24586         Roo.EventManager.on(this.doc, {
24587             //'mousedown': this.onEditorEvent,
24588             'mouseup': this.onEditorEvent,
24589             'dblclick': this.onEditorEvent,
24590             'click': this.onEditorEvent,
24591             'keyup': this.onEditorEvent,
24592             buffer:100,
24593             scope: this
24594         });
24595         if(Roo.isGecko){
24596             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24597         }
24598         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24599             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24600         }
24601         this.initialized = true;
24602
24603         this.owner.fireEvent('initialize', this);
24604         this.pushValue();
24605     },
24606
24607     // private
24608     onDestroy : function(){
24609         
24610         
24611         
24612         if(this.rendered){
24613             
24614             //for (var i =0; i < this.toolbars.length;i++) {
24615             //    // fixme - ask toolbars for heights?
24616             //    this.toolbars[i].onDestroy();
24617            // }
24618             
24619             //this.wrap.dom.innerHTML = '';
24620             //this.wrap.remove();
24621         }
24622     },
24623
24624     // private
24625     onFirstFocus : function(){
24626         
24627         this.assignDocWin();
24628         
24629         
24630         this.activated = true;
24631          
24632     
24633         if(Roo.isGecko){ // prevent silly gecko errors
24634             this.win.focus();
24635             var s = this.win.getSelection();
24636             if(!s.focusNode || s.focusNode.nodeType != 3){
24637                 var r = s.getRangeAt(0);
24638                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24639                 r.collapse(true);
24640                 this.deferFocus();
24641             }
24642             try{
24643                 this.execCmd('useCSS', true);
24644                 this.execCmd('styleWithCSS', false);
24645             }catch(e){}
24646         }
24647         this.owner.fireEvent('activate', this);
24648     },
24649
24650     // private
24651     adjustFont: function(btn){
24652         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24653         //if(Roo.isSafari){ // safari
24654         //    adjust *= 2;
24655        // }
24656         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24657         if(Roo.isSafari){ // safari
24658             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24659             v =  (v < 10) ? 10 : v;
24660             v =  (v > 48) ? 48 : v;
24661             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24662             
24663         }
24664         
24665         
24666         v = Math.max(1, v+adjust);
24667         
24668         this.execCmd('FontSize', v  );
24669     },
24670
24671     onEditorEvent : function(e)
24672     {
24673         this.owner.fireEvent('editorevent', this, e);
24674       //  this.updateToolbar();
24675         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24676     },
24677
24678     insertTag : function(tg)
24679     {
24680         // could be a bit smarter... -> wrap the current selected tRoo..
24681         if (tg.toLowerCase() == 'span' ||
24682             tg.toLowerCase() == 'code' ||
24683             tg.toLowerCase() == 'sup' ||
24684             tg.toLowerCase() == 'sub' 
24685             ) {
24686             
24687             range = this.createRange(this.getSelection());
24688             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24689             wrappingNode.appendChild(range.extractContents());
24690             range.insertNode(wrappingNode);
24691
24692             return;
24693             
24694             
24695             
24696         }
24697         this.execCmd("formatblock",   tg);
24698         
24699     },
24700     
24701     insertText : function(txt)
24702     {
24703         
24704         
24705         var range = this.createRange();
24706         range.deleteContents();
24707                //alert(Sender.getAttribute('label'));
24708                
24709         range.insertNode(this.doc.createTextNode(txt));
24710     } ,
24711     
24712      
24713
24714     /**
24715      * Executes a Midas editor command on the editor document and performs necessary focus and
24716      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24717      * @param {String} cmd The Midas command
24718      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24719      */
24720     relayCmd : function(cmd, value){
24721         this.win.focus();
24722         this.execCmd(cmd, value);
24723         this.owner.fireEvent('editorevent', this);
24724         //this.updateToolbar();
24725         this.owner.deferFocus();
24726     },
24727
24728     /**
24729      * Executes a Midas editor command directly on the editor document.
24730      * For visual commands, you should use {@link #relayCmd} instead.
24731      * <b>This should only be called after the editor is initialized.</b>
24732      * @param {String} cmd The Midas command
24733      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24734      */
24735     execCmd : function(cmd, value){
24736         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24737         this.syncValue();
24738     },
24739  
24740  
24741    
24742     /**
24743      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24744      * to insert tRoo.
24745      * @param {String} text | dom node.. 
24746      */
24747     insertAtCursor : function(text)
24748     {
24749         
24750         if(!this.activated){
24751             return;
24752         }
24753         /*
24754         if(Roo.isIE){
24755             this.win.focus();
24756             var r = this.doc.selection.createRange();
24757             if(r){
24758                 r.collapse(true);
24759                 r.pasteHTML(text);
24760                 this.syncValue();
24761                 this.deferFocus();
24762             
24763             }
24764             return;
24765         }
24766         */
24767         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24768             this.win.focus();
24769             
24770             
24771             // from jquery ui (MIT licenced)
24772             var range, node;
24773             var win = this.win;
24774             
24775             if (win.getSelection && win.getSelection().getRangeAt) {
24776                 range = win.getSelection().getRangeAt(0);
24777                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24778                 range.insertNode(node);
24779             } else if (win.document.selection && win.document.selection.createRange) {
24780                 // no firefox support
24781                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24782                 win.document.selection.createRange().pasteHTML(txt);
24783             } else {
24784                 // no firefox support
24785                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24786                 this.execCmd('InsertHTML', txt);
24787             } 
24788             
24789             this.syncValue();
24790             
24791             this.deferFocus();
24792         }
24793     },
24794  // private
24795     mozKeyPress : function(e){
24796         if(e.ctrlKey){
24797             var c = e.getCharCode(), cmd;
24798           
24799             if(c > 0){
24800                 c = String.fromCharCode(c).toLowerCase();
24801                 switch(c){
24802                     case 'b':
24803                         cmd = 'bold';
24804                         break;
24805                     case 'i':
24806                         cmd = 'italic';
24807                         break;
24808                     
24809                     case 'u':
24810                         cmd = 'underline';
24811                         break;
24812                     
24813                     case 'v':
24814                         this.cleanUpPaste.defer(100, this);
24815                         return;
24816                         
24817                 }
24818                 if(cmd){
24819                     this.win.focus();
24820                     this.execCmd(cmd);
24821                     this.deferFocus();
24822                     e.preventDefault();
24823                 }
24824                 
24825             }
24826         }
24827     },
24828
24829     // private
24830     fixKeys : function(){ // load time branching for fastest keydown performance
24831         if(Roo.isIE){
24832             return function(e){
24833                 var k = e.getKey(), r;
24834                 if(k == e.TAB){
24835                     e.stopEvent();
24836                     r = this.doc.selection.createRange();
24837                     if(r){
24838                         r.collapse(true);
24839                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24840                         this.deferFocus();
24841                     }
24842                     return;
24843                 }
24844                 
24845                 if(k == e.ENTER){
24846                     r = this.doc.selection.createRange();
24847                     if(r){
24848                         var target = r.parentElement();
24849                         if(!target || target.tagName.toLowerCase() != 'li'){
24850                             e.stopEvent();
24851                             r.pasteHTML('<br />');
24852                             r.collapse(false);
24853                             r.select();
24854                         }
24855                     }
24856                 }
24857                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24858                     this.cleanUpPaste.defer(100, this);
24859                     return;
24860                 }
24861                 
24862                 
24863             };
24864         }else if(Roo.isOpera){
24865             return function(e){
24866                 var k = e.getKey();
24867                 if(k == e.TAB){
24868                     e.stopEvent();
24869                     this.win.focus();
24870                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24871                     this.deferFocus();
24872                 }
24873                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24874                     this.cleanUpPaste.defer(100, this);
24875                     return;
24876                 }
24877                 
24878             };
24879         }else if(Roo.isSafari){
24880             return function(e){
24881                 var k = e.getKey();
24882                 
24883                 if(k == e.TAB){
24884                     e.stopEvent();
24885                     this.execCmd('InsertText','\t');
24886                     this.deferFocus();
24887                     return;
24888                 }
24889                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24890                     this.cleanUpPaste.defer(100, this);
24891                     return;
24892                 }
24893                 
24894              };
24895         }
24896     }(),
24897     
24898     getAllAncestors: function()
24899     {
24900         var p = this.getSelectedNode();
24901         var a = [];
24902         if (!p) {
24903             a.push(p); // push blank onto stack..
24904             p = this.getParentElement();
24905         }
24906         
24907         
24908         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24909             a.push(p);
24910             p = p.parentNode;
24911         }
24912         a.push(this.doc.body);
24913         return a;
24914     },
24915     lastSel : false,
24916     lastSelNode : false,
24917     
24918     
24919     getSelection : function() 
24920     {
24921         this.assignDocWin();
24922         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24923     },
24924     
24925     getSelectedNode: function() 
24926     {
24927         // this may only work on Gecko!!!
24928         
24929         // should we cache this!!!!
24930         
24931         
24932         
24933          
24934         var range = this.createRange(this.getSelection()).cloneRange();
24935         
24936         if (Roo.isIE) {
24937             var parent = range.parentElement();
24938             while (true) {
24939                 var testRange = range.duplicate();
24940                 testRange.moveToElementText(parent);
24941                 if (testRange.inRange(range)) {
24942                     break;
24943                 }
24944                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24945                     break;
24946                 }
24947                 parent = parent.parentElement;
24948             }
24949             return parent;
24950         }
24951         
24952         // is ancestor a text element.
24953         var ac =  range.commonAncestorContainer;
24954         if (ac.nodeType == 3) {
24955             ac = ac.parentNode;
24956         }
24957         
24958         var ar = ac.childNodes;
24959          
24960         var nodes = [];
24961         var other_nodes = [];
24962         var has_other_nodes = false;
24963         for (var i=0;i<ar.length;i++) {
24964             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24965                 continue;
24966             }
24967             // fullly contained node.
24968             
24969             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24970                 nodes.push(ar[i]);
24971                 continue;
24972             }
24973             
24974             // probably selected..
24975             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24976                 other_nodes.push(ar[i]);
24977                 continue;
24978             }
24979             // outer..
24980             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24981                 continue;
24982             }
24983             
24984             
24985             has_other_nodes = true;
24986         }
24987         if (!nodes.length && other_nodes.length) {
24988             nodes= other_nodes;
24989         }
24990         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24991             return false;
24992         }
24993         
24994         return nodes[0];
24995     },
24996     createRange: function(sel)
24997     {
24998         // this has strange effects when using with 
24999         // top toolbar - not sure if it's a great idea.
25000         //this.editor.contentWindow.focus();
25001         if (typeof sel != "undefined") {
25002             try {
25003                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25004             } catch(e) {
25005                 return this.doc.createRange();
25006             }
25007         } else {
25008             return this.doc.createRange();
25009         }
25010     },
25011     getParentElement: function()
25012     {
25013         
25014         this.assignDocWin();
25015         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25016         
25017         var range = this.createRange(sel);
25018          
25019         try {
25020             var p = range.commonAncestorContainer;
25021             while (p.nodeType == 3) { // text node
25022                 p = p.parentNode;
25023             }
25024             return p;
25025         } catch (e) {
25026             return null;
25027         }
25028     
25029     },
25030     /***
25031      *
25032      * Range intersection.. the hard stuff...
25033      *  '-1' = before
25034      *  '0' = hits..
25035      *  '1' = after.
25036      *         [ -- selected range --- ]
25037      *   [fail]                        [fail]
25038      *
25039      *    basically..
25040      *      if end is before start or  hits it. fail.
25041      *      if start is after end or hits it fail.
25042      *
25043      *   if either hits (but other is outside. - then it's not 
25044      *   
25045      *    
25046      **/
25047     
25048     
25049     // @see http://www.thismuchiknow.co.uk/?p=64.
25050     rangeIntersectsNode : function(range, node)
25051     {
25052         var nodeRange = node.ownerDocument.createRange();
25053         try {
25054             nodeRange.selectNode(node);
25055         } catch (e) {
25056             nodeRange.selectNodeContents(node);
25057         }
25058     
25059         var rangeStartRange = range.cloneRange();
25060         rangeStartRange.collapse(true);
25061     
25062         var rangeEndRange = range.cloneRange();
25063         rangeEndRange.collapse(false);
25064     
25065         var nodeStartRange = nodeRange.cloneRange();
25066         nodeStartRange.collapse(true);
25067     
25068         var nodeEndRange = nodeRange.cloneRange();
25069         nodeEndRange.collapse(false);
25070     
25071         return rangeStartRange.compareBoundaryPoints(
25072                  Range.START_TO_START, nodeEndRange) == -1 &&
25073                rangeEndRange.compareBoundaryPoints(
25074                  Range.START_TO_START, nodeStartRange) == 1;
25075         
25076          
25077     },
25078     rangeCompareNode : function(range, node)
25079     {
25080         var nodeRange = node.ownerDocument.createRange();
25081         try {
25082             nodeRange.selectNode(node);
25083         } catch (e) {
25084             nodeRange.selectNodeContents(node);
25085         }
25086         
25087         
25088         range.collapse(true);
25089     
25090         nodeRange.collapse(true);
25091      
25092         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25093         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25094          
25095         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25096         
25097         var nodeIsBefore   =  ss == 1;
25098         var nodeIsAfter    = ee == -1;
25099         
25100         if (nodeIsBefore && nodeIsAfter) {
25101             return 0; // outer
25102         }
25103         if (!nodeIsBefore && nodeIsAfter) {
25104             return 1; //right trailed.
25105         }
25106         
25107         if (nodeIsBefore && !nodeIsAfter) {
25108             return 2;  // left trailed.
25109         }
25110         // fully contined.
25111         return 3;
25112     },
25113
25114     // private? - in a new class?
25115     cleanUpPaste :  function()
25116     {
25117         // cleans up the whole document..
25118         Roo.log('cleanuppaste');
25119         
25120         this.cleanUpChildren(this.doc.body);
25121         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25122         if (clean != this.doc.body.innerHTML) {
25123             this.doc.body.innerHTML = clean;
25124         }
25125         
25126     },
25127     
25128     cleanWordChars : function(input) {// change the chars to hex code
25129         var he = Roo.HtmlEditorCore;
25130         
25131         var output = input;
25132         Roo.each(he.swapCodes, function(sw) { 
25133             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25134             
25135             output = output.replace(swapper, sw[1]);
25136         });
25137         
25138         return output;
25139     },
25140     
25141     
25142     cleanUpChildren : function (n)
25143     {
25144         if (!n.childNodes.length) {
25145             return;
25146         }
25147         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25148            this.cleanUpChild(n.childNodes[i]);
25149         }
25150     },
25151     
25152     
25153         
25154     
25155     cleanUpChild : function (node)
25156     {
25157         var ed = this;
25158         //console.log(node);
25159         if (node.nodeName == "#text") {
25160             // clean up silly Windows -- stuff?
25161             return; 
25162         }
25163         if (node.nodeName == "#comment") {
25164             node.parentNode.removeChild(node);
25165             // clean up silly Windows -- stuff?
25166             return; 
25167         }
25168         var lcname = node.tagName.toLowerCase();
25169         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25170         // whitelist of tags..
25171         
25172         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25173             // remove node.
25174             node.parentNode.removeChild(node);
25175             return;
25176             
25177         }
25178         
25179         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25180         
25181         // spans with no attributes - just remove them..
25182         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25183             remove_keep_children = true;
25184         }
25185         
25186         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25187         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25188         
25189         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25190         //    remove_keep_children = true;
25191         //}
25192         
25193         if (remove_keep_children) {
25194             this.cleanUpChildren(node);
25195             // inserts everything just before this node...
25196             while (node.childNodes.length) {
25197                 var cn = node.childNodes[0];
25198                 node.removeChild(cn);
25199                 node.parentNode.insertBefore(cn, node);
25200             }
25201             node.parentNode.removeChild(node);
25202             return;
25203         }
25204         
25205         if (!node.attributes || !node.attributes.length) {
25206             
25207           
25208             
25209             
25210             this.cleanUpChildren(node);
25211             return;
25212         }
25213         
25214         function cleanAttr(n,v)
25215         {
25216             
25217             if (v.match(/^\./) || v.match(/^\//)) {
25218                 return;
25219             }
25220             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25221                 return;
25222             }
25223             if (v.match(/^#/)) {
25224                 return;
25225             }
25226             if (v.match(/^\{/)) { // allow template editing.
25227                 return;
25228             }
25229 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25230             node.removeAttribute(n);
25231             
25232         }
25233         
25234         var cwhite = this.cwhite;
25235         var cblack = this.cblack;
25236             
25237         function cleanStyle(n,v)
25238         {
25239             if (v.match(/expression/)) { //XSS?? should we even bother..
25240                 node.removeAttribute(n);
25241                 return;
25242             }
25243             
25244             var parts = v.split(/;/);
25245             var clean = [];
25246             
25247             Roo.each(parts, function(p) {
25248                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25249                 if (!p.length) {
25250                     return true;
25251                 }
25252                 var l = p.split(':').shift().replace(/\s+/g,'');
25253                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25254                 
25255                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25256 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25257                     //node.removeAttribute(n);
25258                     return true;
25259                 }
25260                 //Roo.log()
25261                 // only allow 'c whitelisted system attributes'
25262                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25263 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25264                     //node.removeAttribute(n);
25265                     return true;
25266                 }
25267                 
25268                 
25269                  
25270                 
25271                 clean.push(p);
25272                 return true;
25273             });
25274             if (clean.length) { 
25275                 node.setAttribute(n, clean.join(';'));
25276             } else {
25277                 node.removeAttribute(n);
25278             }
25279             
25280         }
25281         
25282         
25283         for (var i = node.attributes.length-1; i > -1 ; i--) {
25284             var a = node.attributes[i];
25285             //console.log(a);
25286             
25287             if (a.name.toLowerCase().substr(0,2)=='on')  {
25288                 node.removeAttribute(a.name);
25289                 continue;
25290             }
25291             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25292                 node.removeAttribute(a.name);
25293                 continue;
25294             }
25295             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25296                 cleanAttr(a.name,a.value); // fixme..
25297                 continue;
25298             }
25299             if (a.name == 'style') {
25300                 cleanStyle(a.name,a.value);
25301                 continue;
25302             }
25303             /// clean up MS crap..
25304             // tecnically this should be a list of valid class'es..
25305             
25306             
25307             if (a.name == 'class') {
25308                 if (a.value.match(/^Mso/)) {
25309                     node.removeAttribute('class');
25310                 }
25311                 
25312                 if (a.value.match(/^body$/)) {
25313                     node.removeAttribute('class');
25314                 }
25315                 continue;
25316             }
25317             
25318             // style cleanup!?
25319             // class cleanup?
25320             
25321         }
25322         
25323         
25324         this.cleanUpChildren(node);
25325         
25326         
25327     },
25328     
25329     /**
25330      * Clean up MS wordisms...
25331      */
25332     cleanWord : function(node)
25333     {
25334         if (!node) {
25335             this.cleanWord(this.doc.body);
25336             return;
25337         }
25338         
25339         if(
25340                 node.nodeName == 'SPAN' &&
25341                 !node.hasAttributes() &&
25342                 node.childNodes.length == 1 &&
25343                 node.firstChild.nodeName == "#text"  
25344         ) {
25345             var textNode = node.firstChild;
25346             node.removeChild(textNode);
25347             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25348                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25349             }
25350             node.parentNode.insertBefore(textNode, node);
25351             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25352                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25353             }
25354             node.parentNode.removeChild(node);
25355         }
25356         
25357         if (node.nodeName == "#text") {
25358             // clean up silly Windows -- stuff?
25359             return; 
25360         }
25361         if (node.nodeName == "#comment") {
25362             node.parentNode.removeChild(node);
25363             // clean up silly Windows -- stuff?
25364             return; 
25365         }
25366         
25367         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25368             node.parentNode.removeChild(node);
25369             return;
25370         }
25371         //Roo.log(node.tagName);
25372         // remove - but keep children..
25373         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25374             //Roo.log('-- removed');
25375             while (node.childNodes.length) {
25376                 var cn = node.childNodes[0];
25377                 node.removeChild(cn);
25378                 node.parentNode.insertBefore(cn, node);
25379                 // move node to parent - and clean it..
25380                 this.cleanWord(cn);
25381             }
25382             node.parentNode.removeChild(node);
25383             /// no need to iterate chidlren = it's got none..
25384             //this.iterateChildren(node, this.cleanWord);
25385             return;
25386         }
25387         // clean styles
25388         if (node.className.length) {
25389             
25390             var cn = node.className.split(/\W+/);
25391             var cna = [];
25392             Roo.each(cn, function(cls) {
25393                 if (cls.match(/Mso[a-zA-Z]+/)) {
25394                     return;
25395                 }
25396                 cna.push(cls);
25397             });
25398             node.className = cna.length ? cna.join(' ') : '';
25399             if (!cna.length) {
25400                 node.removeAttribute("class");
25401             }
25402         }
25403         
25404         if (node.hasAttribute("lang")) {
25405             node.removeAttribute("lang");
25406         }
25407         
25408         if (node.hasAttribute("style")) {
25409             
25410             var styles = node.getAttribute("style").split(";");
25411             var nstyle = [];
25412             Roo.each(styles, function(s) {
25413                 if (!s.match(/:/)) {
25414                     return;
25415                 }
25416                 var kv = s.split(":");
25417                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25418                     return;
25419                 }
25420                 // what ever is left... we allow.
25421                 nstyle.push(s);
25422             });
25423             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25424             if (!nstyle.length) {
25425                 node.removeAttribute('style');
25426             }
25427         }
25428         this.iterateChildren(node, this.cleanWord);
25429         
25430         
25431         
25432     },
25433     /**
25434      * iterateChildren of a Node, calling fn each time, using this as the scole..
25435      * @param {DomNode} node node to iterate children of.
25436      * @param {Function} fn method of this class to call on each item.
25437      */
25438     iterateChildren : function(node, fn)
25439     {
25440         if (!node.childNodes.length) {
25441                 return;
25442         }
25443         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25444            fn.call(this, node.childNodes[i])
25445         }
25446     },
25447     
25448     
25449     /**
25450      * cleanTableWidths.
25451      *
25452      * Quite often pasting from word etc.. results in tables with column and widths.
25453      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25454      *
25455      */
25456     cleanTableWidths : function(node)
25457     {
25458          
25459          
25460         if (!node) {
25461             this.cleanTableWidths(this.doc.body);
25462             return;
25463         }
25464         
25465         // ignore list...
25466         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25467             return; 
25468         }
25469         Roo.log(node.tagName);
25470         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25471             this.iterateChildren(node, this.cleanTableWidths);
25472             return;
25473         }
25474         if (node.hasAttribute('width')) {
25475             node.removeAttribute('width');
25476         }
25477         
25478          
25479         if (node.hasAttribute("style")) {
25480             // pretty basic...
25481             
25482             var styles = node.getAttribute("style").split(";");
25483             var nstyle = [];
25484             Roo.each(styles, function(s) {
25485                 if (!s.match(/:/)) {
25486                     return;
25487                 }
25488                 var kv = s.split(":");
25489                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25490                     return;
25491                 }
25492                 // what ever is left... we allow.
25493                 nstyle.push(s);
25494             });
25495             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25496             if (!nstyle.length) {
25497                 node.removeAttribute('style');
25498             }
25499         }
25500         
25501         this.iterateChildren(node, this.cleanTableWidths);
25502         
25503         
25504     },
25505     
25506     
25507     
25508     
25509     domToHTML : function(currentElement, depth, nopadtext) {
25510         
25511         depth = depth || 0;
25512         nopadtext = nopadtext || false;
25513     
25514         if (!currentElement) {
25515             return this.domToHTML(this.doc.body);
25516         }
25517         
25518         //Roo.log(currentElement);
25519         var j;
25520         var allText = false;
25521         var nodeName = currentElement.nodeName;
25522         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25523         
25524         if  (nodeName == '#text') {
25525             
25526             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25527         }
25528         
25529         
25530         var ret = '';
25531         if (nodeName != 'BODY') {
25532              
25533             var i = 0;
25534             // Prints the node tagName, such as <A>, <IMG>, etc
25535             if (tagName) {
25536                 var attr = [];
25537                 for(i = 0; i < currentElement.attributes.length;i++) {
25538                     // quoting?
25539                     var aname = currentElement.attributes.item(i).name;
25540                     if (!currentElement.attributes.item(i).value.length) {
25541                         continue;
25542                     }
25543                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25544                 }
25545                 
25546                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25547             } 
25548             else {
25549                 
25550                 // eack
25551             }
25552         } else {
25553             tagName = false;
25554         }
25555         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25556             return ret;
25557         }
25558         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25559             nopadtext = true;
25560         }
25561         
25562         
25563         // Traverse the tree
25564         i = 0;
25565         var currentElementChild = currentElement.childNodes.item(i);
25566         var allText = true;
25567         var innerHTML  = '';
25568         lastnode = '';
25569         while (currentElementChild) {
25570             // Formatting code (indent the tree so it looks nice on the screen)
25571             var nopad = nopadtext;
25572             if (lastnode == 'SPAN') {
25573                 nopad  = true;
25574             }
25575             // text
25576             if  (currentElementChild.nodeName == '#text') {
25577                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25578                 toadd = nopadtext ? toadd : toadd.trim();
25579                 if (!nopad && toadd.length > 80) {
25580                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25581                 }
25582                 innerHTML  += toadd;
25583                 
25584                 i++;
25585                 currentElementChild = currentElement.childNodes.item(i);
25586                 lastNode = '';
25587                 continue;
25588             }
25589             allText = false;
25590             
25591             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25592                 
25593             // Recursively traverse the tree structure of the child node
25594             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25595             lastnode = currentElementChild.nodeName;
25596             i++;
25597             currentElementChild=currentElement.childNodes.item(i);
25598         }
25599         
25600         ret += innerHTML;
25601         
25602         if (!allText) {
25603                 // The remaining code is mostly for formatting the tree
25604             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25605         }
25606         
25607         
25608         if (tagName) {
25609             ret+= "</"+tagName+">";
25610         }
25611         return ret;
25612         
25613     },
25614         
25615     applyBlacklists : function()
25616     {
25617         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25618         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25619         
25620         this.white = [];
25621         this.black = [];
25622         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25623             if (b.indexOf(tag) > -1) {
25624                 return;
25625             }
25626             this.white.push(tag);
25627             
25628         }, this);
25629         
25630         Roo.each(w, function(tag) {
25631             if (b.indexOf(tag) > -1) {
25632                 return;
25633             }
25634             if (this.white.indexOf(tag) > -1) {
25635                 return;
25636             }
25637             this.white.push(tag);
25638             
25639         }, this);
25640         
25641         
25642         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25643             if (w.indexOf(tag) > -1) {
25644                 return;
25645             }
25646             this.black.push(tag);
25647             
25648         }, this);
25649         
25650         Roo.each(b, function(tag) {
25651             if (w.indexOf(tag) > -1) {
25652                 return;
25653             }
25654             if (this.black.indexOf(tag) > -1) {
25655                 return;
25656             }
25657             this.black.push(tag);
25658             
25659         }, this);
25660         
25661         
25662         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25663         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25664         
25665         this.cwhite = [];
25666         this.cblack = [];
25667         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25668             if (b.indexOf(tag) > -1) {
25669                 return;
25670             }
25671             this.cwhite.push(tag);
25672             
25673         }, this);
25674         
25675         Roo.each(w, function(tag) {
25676             if (b.indexOf(tag) > -1) {
25677                 return;
25678             }
25679             if (this.cwhite.indexOf(tag) > -1) {
25680                 return;
25681             }
25682             this.cwhite.push(tag);
25683             
25684         }, this);
25685         
25686         
25687         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25688             if (w.indexOf(tag) > -1) {
25689                 return;
25690             }
25691             this.cblack.push(tag);
25692             
25693         }, this);
25694         
25695         Roo.each(b, function(tag) {
25696             if (w.indexOf(tag) > -1) {
25697                 return;
25698             }
25699             if (this.cblack.indexOf(tag) > -1) {
25700                 return;
25701             }
25702             this.cblack.push(tag);
25703             
25704         }, this);
25705     },
25706     
25707     setStylesheets : function(stylesheets)
25708     {
25709         if(typeof(stylesheets) == 'string'){
25710             Roo.get(this.iframe.contentDocument.head).createChild({
25711                 tag : 'link',
25712                 rel : 'stylesheet',
25713                 type : 'text/css',
25714                 href : stylesheets
25715             });
25716             
25717             return;
25718         }
25719         var _this = this;
25720      
25721         Roo.each(stylesheets, function(s) {
25722             if(!s.length){
25723                 return;
25724             }
25725             
25726             Roo.get(_this.iframe.contentDocument.head).createChild({
25727                 tag : 'link',
25728                 rel : 'stylesheet',
25729                 type : 'text/css',
25730                 href : s
25731             });
25732         });
25733
25734         
25735     },
25736     
25737     removeStylesheets : function()
25738     {
25739         var _this = this;
25740         
25741         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25742             s.remove();
25743         });
25744     },
25745     
25746     setStyle : function(style)
25747     {
25748         Roo.get(this.iframe.contentDocument.head).createChild({
25749             tag : 'style',
25750             type : 'text/css',
25751             html : style
25752         });
25753
25754         return;
25755     }
25756     
25757     // hide stuff that is not compatible
25758     /**
25759      * @event blur
25760      * @hide
25761      */
25762     /**
25763      * @event change
25764      * @hide
25765      */
25766     /**
25767      * @event focus
25768      * @hide
25769      */
25770     /**
25771      * @event specialkey
25772      * @hide
25773      */
25774     /**
25775      * @cfg {String} fieldClass @hide
25776      */
25777     /**
25778      * @cfg {String} focusClass @hide
25779      */
25780     /**
25781      * @cfg {String} autoCreate @hide
25782      */
25783     /**
25784      * @cfg {String} inputType @hide
25785      */
25786     /**
25787      * @cfg {String} invalidClass @hide
25788      */
25789     /**
25790      * @cfg {String} invalidText @hide
25791      */
25792     /**
25793      * @cfg {String} msgFx @hide
25794      */
25795     /**
25796      * @cfg {String} validateOnBlur @hide
25797      */
25798 });
25799
25800 Roo.HtmlEditorCore.white = [
25801         'area', 'br', 'img', 'input', 'hr', 'wbr',
25802         
25803        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25804        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25805        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25806        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25807        'table',   'ul',         'xmp', 
25808        
25809        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25810       'thead',   'tr', 
25811      
25812       'dir', 'menu', 'ol', 'ul', 'dl',
25813        
25814       'embed',  'object'
25815 ];
25816
25817
25818 Roo.HtmlEditorCore.black = [
25819     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25820         'applet', // 
25821         'base',   'basefont', 'bgsound', 'blink',  'body', 
25822         'frame',  'frameset', 'head',    'html',   'ilayer', 
25823         'iframe', 'layer',  'link',     'meta',    'object',   
25824         'script', 'style' ,'title',  'xml' // clean later..
25825 ];
25826 Roo.HtmlEditorCore.clean = [
25827     'script', 'style', 'title', 'xml'
25828 ];
25829 Roo.HtmlEditorCore.remove = [
25830     'font'
25831 ];
25832 // attributes..
25833
25834 Roo.HtmlEditorCore.ablack = [
25835     'on'
25836 ];
25837     
25838 Roo.HtmlEditorCore.aclean = [ 
25839     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25840 ];
25841
25842 // protocols..
25843 Roo.HtmlEditorCore.pwhite= [
25844         'http',  'https',  'mailto'
25845 ];
25846
25847 // white listed style attributes.
25848 Roo.HtmlEditorCore.cwhite= [
25849       //  'text-align', /// default is to allow most things..
25850       
25851          
25852 //        'font-size'//??
25853 ];
25854
25855 // black listed style attributes.
25856 Roo.HtmlEditorCore.cblack= [
25857       //  'font-size' -- this can be set by the project 
25858 ];
25859
25860
25861 Roo.HtmlEditorCore.swapCodes   =[ 
25862     [    8211, "&#8211;" ], 
25863     [    8212, "&#8212;" ], 
25864     [    8216,  "'" ],  
25865     [    8217, "'" ],  
25866     [    8220, '"' ],  
25867     [    8221, '"' ],  
25868     [    8226, "*" ],  
25869     [    8230, "..." ]
25870 ]; 
25871
25872     /*
25873  * - LGPL
25874  *
25875  * HtmlEditor
25876  * 
25877  */
25878
25879 /**
25880  * @class Roo.bootstrap.HtmlEditor
25881  * @extends Roo.bootstrap.TextArea
25882  * Bootstrap HtmlEditor class
25883
25884  * @constructor
25885  * Create a new HtmlEditor
25886  * @param {Object} config The config object
25887  */
25888
25889 Roo.bootstrap.HtmlEditor = function(config){
25890     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25891     if (!this.toolbars) {
25892         this.toolbars = [];
25893     }
25894     
25895     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25896     this.addEvents({
25897             /**
25898              * @event initialize
25899              * Fires when the editor is fully initialized (including the iframe)
25900              * @param {HtmlEditor} this
25901              */
25902             initialize: true,
25903             /**
25904              * @event activate
25905              * Fires when the editor is first receives the focus. Any insertion must wait
25906              * until after this event.
25907              * @param {HtmlEditor} this
25908              */
25909             activate: true,
25910              /**
25911              * @event beforesync
25912              * Fires before the textarea is updated with content from the editor iframe. Return false
25913              * to cancel the sync.
25914              * @param {HtmlEditor} this
25915              * @param {String} html
25916              */
25917             beforesync: true,
25918              /**
25919              * @event beforepush
25920              * Fires before the iframe editor is updated with content from the textarea. Return false
25921              * to cancel the push.
25922              * @param {HtmlEditor} this
25923              * @param {String} html
25924              */
25925             beforepush: true,
25926              /**
25927              * @event sync
25928              * Fires when the textarea is updated with content from the editor iframe.
25929              * @param {HtmlEditor} this
25930              * @param {String} html
25931              */
25932             sync: true,
25933              /**
25934              * @event push
25935              * Fires when the iframe editor is updated with content from the textarea.
25936              * @param {HtmlEditor} this
25937              * @param {String} html
25938              */
25939             push: true,
25940              /**
25941              * @event editmodechange
25942              * Fires when the editor switches edit modes
25943              * @param {HtmlEditor} this
25944              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25945              */
25946             editmodechange: true,
25947             /**
25948              * @event editorevent
25949              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25950              * @param {HtmlEditor} this
25951              */
25952             editorevent: true,
25953             /**
25954              * @event firstfocus
25955              * Fires when on first focus - needed by toolbars..
25956              * @param {HtmlEditor} this
25957              */
25958             firstfocus: true,
25959             /**
25960              * @event autosave
25961              * Auto save the htmlEditor value as a file into Events
25962              * @param {HtmlEditor} this
25963              */
25964             autosave: true,
25965             /**
25966              * @event savedpreview
25967              * preview the saved version of htmlEditor
25968              * @param {HtmlEditor} this
25969              */
25970             savedpreview: true
25971         });
25972 };
25973
25974
25975 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25976     
25977     
25978       /**
25979      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25980      */
25981     toolbars : false,
25982     
25983      /**
25984     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25985     */
25986     btns : [],
25987    
25988      /**
25989      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25990      *                        Roo.resizable.
25991      */
25992     resizable : false,
25993      /**
25994      * @cfg {Number} height (in pixels)
25995      */   
25996     height: 300,
25997    /**
25998      * @cfg {Number} width (in pixels)
25999      */   
26000     width: false,
26001     
26002     /**
26003      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26004      * 
26005      */
26006     stylesheets: false,
26007     
26008     // id of frame..
26009     frameId: false,
26010     
26011     // private properties
26012     validationEvent : false,
26013     deferHeight: true,
26014     initialized : false,
26015     activated : false,
26016     
26017     onFocus : Roo.emptyFn,
26018     iframePad:3,
26019     hideMode:'offsets',
26020     
26021     tbContainer : false,
26022     
26023     bodyCls : '',
26024     
26025     toolbarContainer :function() {
26026         return this.wrap.select('.x-html-editor-tb',true).first();
26027     },
26028
26029     /**
26030      * Protected method that will not generally be called directly. It
26031      * is called when the editor creates its toolbar. Override this method if you need to
26032      * add custom toolbar buttons.
26033      * @param {HtmlEditor} editor
26034      */
26035     createToolbar : function(){
26036         Roo.log('renewing');
26037         Roo.log("create toolbars");
26038         
26039         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26040         this.toolbars[0].render(this.toolbarContainer());
26041         
26042         return;
26043         
26044 //        if (!editor.toolbars || !editor.toolbars.length) {
26045 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26046 //        }
26047 //        
26048 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26049 //            editor.toolbars[i] = Roo.factory(
26050 //                    typeof(editor.toolbars[i]) == 'string' ?
26051 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26052 //                Roo.bootstrap.HtmlEditor);
26053 //            editor.toolbars[i].init(editor);
26054 //        }
26055     },
26056
26057      
26058     // private
26059     onRender : function(ct, position)
26060     {
26061        // Roo.log("Call onRender: " + this.xtype);
26062         var _t = this;
26063         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26064       
26065         this.wrap = this.inputEl().wrap({
26066             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26067         });
26068         
26069         this.editorcore.onRender(ct, position);
26070          
26071         if (this.resizable) {
26072             this.resizeEl = new Roo.Resizable(this.wrap, {
26073                 pinned : true,
26074                 wrap: true,
26075                 dynamic : true,
26076                 minHeight : this.height,
26077                 height: this.height,
26078                 handles : this.resizable,
26079                 width: this.width,
26080                 listeners : {
26081                     resize : function(r, w, h) {
26082                         _t.onResize(w,h); // -something
26083                     }
26084                 }
26085             });
26086             
26087         }
26088         this.createToolbar(this);
26089        
26090         
26091         if(!this.width && this.resizable){
26092             this.setSize(this.wrap.getSize());
26093         }
26094         if (this.resizeEl) {
26095             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26096             // should trigger onReize..
26097         }
26098         
26099     },
26100
26101     // private
26102     onResize : function(w, h)
26103     {
26104         Roo.log('resize: ' +w + ',' + h );
26105         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26106         var ew = false;
26107         var eh = false;
26108         
26109         if(this.inputEl() ){
26110             if(typeof w == 'number'){
26111                 var aw = w - this.wrap.getFrameWidth('lr');
26112                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26113                 ew = aw;
26114             }
26115             if(typeof h == 'number'){
26116                  var tbh = -11;  // fixme it needs to tool bar size!
26117                 for (var i =0; i < this.toolbars.length;i++) {
26118                     // fixme - ask toolbars for heights?
26119                     tbh += this.toolbars[i].el.getHeight();
26120                     //if (this.toolbars[i].footer) {
26121                     //    tbh += this.toolbars[i].footer.el.getHeight();
26122                     //}
26123                 }
26124               
26125                 
26126                 
26127                 
26128                 
26129                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26130                 ah -= 5; // knock a few pixes off for look..
26131                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26132                 var eh = ah;
26133             }
26134         }
26135         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26136         this.editorcore.onResize(ew,eh);
26137         
26138     },
26139
26140     /**
26141      * Toggles the editor between standard and source edit mode.
26142      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26143      */
26144     toggleSourceEdit : function(sourceEditMode)
26145     {
26146         this.editorcore.toggleSourceEdit(sourceEditMode);
26147         
26148         if(this.editorcore.sourceEditMode){
26149             Roo.log('editor - showing textarea');
26150             
26151 //            Roo.log('in');
26152 //            Roo.log(this.syncValue());
26153             this.syncValue();
26154             this.inputEl().removeClass(['hide', 'x-hidden']);
26155             this.inputEl().dom.removeAttribute('tabIndex');
26156             this.inputEl().focus();
26157         }else{
26158             Roo.log('editor - hiding textarea');
26159 //            Roo.log('out')
26160 //            Roo.log(this.pushValue()); 
26161             this.pushValue();
26162             
26163             this.inputEl().addClass(['hide', 'x-hidden']);
26164             this.inputEl().dom.setAttribute('tabIndex', -1);
26165             //this.deferFocus();
26166         }
26167          
26168         if(this.resizable){
26169             this.setSize(this.wrap.getSize());
26170         }
26171         
26172         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26173     },
26174  
26175     // private (for BoxComponent)
26176     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26177
26178     // private (for BoxComponent)
26179     getResizeEl : function(){
26180         return this.wrap;
26181     },
26182
26183     // private (for BoxComponent)
26184     getPositionEl : function(){
26185         return this.wrap;
26186     },
26187
26188     // private
26189     initEvents : function(){
26190         this.originalValue = this.getValue();
26191     },
26192
26193 //    /**
26194 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26195 //     * @method
26196 //     */
26197 //    markInvalid : Roo.emptyFn,
26198 //    /**
26199 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26200 //     * @method
26201 //     */
26202 //    clearInvalid : Roo.emptyFn,
26203
26204     setValue : function(v){
26205         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26206         this.editorcore.pushValue();
26207     },
26208
26209      
26210     // private
26211     deferFocus : function(){
26212         this.focus.defer(10, this);
26213     },
26214
26215     // doc'ed in Field
26216     focus : function(){
26217         this.editorcore.focus();
26218         
26219     },
26220       
26221
26222     // private
26223     onDestroy : function(){
26224         
26225         
26226         
26227         if(this.rendered){
26228             
26229             for (var i =0; i < this.toolbars.length;i++) {
26230                 // fixme - ask toolbars for heights?
26231                 this.toolbars[i].onDestroy();
26232             }
26233             
26234             this.wrap.dom.innerHTML = '';
26235             this.wrap.remove();
26236         }
26237     },
26238
26239     // private
26240     onFirstFocus : function(){
26241         //Roo.log("onFirstFocus");
26242         this.editorcore.onFirstFocus();
26243          for (var i =0; i < this.toolbars.length;i++) {
26244             this.toolbars[i].onFirstFocus();
26245         }
26246         
26247     },
26248     
26249     // private
26250     syncValue : function()
26251     {   
26252         this.editorcore.syncValue();
26253     },
26254     
26255     pushValue : function()
26256     {   
26257         this.editorcore.pushValue();
26258     }
26259      
26260     
26261     // hide stuff that is not compatible
26262     /**
26263      * @event blur
26264      * @hide
26265      */
26266     /**
26267      * @event change
26268      * @hide
26269      */
26270     /**
26271      * @event focus
26272      * @hide
26273      */
26274     /**
26275      * @event specialkey
26276      * @hide
26277      */
26278     /**
26279      * @cfg {String} fieldClass @hide
26280      */
26281     /**
26282      * @cfg {String} focusClass @hide
26283      */
26284     /**
26285      * @cfg {String} autoCreate @hide
26286      */
26287     /**
26288      * @cfg {String} inputType @hide
26289      */
26290      
26291     /**
26292      * @cfg {String} invalidText @hide
26293      */
26294     /**
26295      * @cfg {String} msgFx @hide
26296      */
26297     /**
26298      * @cfg {String} validateOnBlur @hide
26299      */
26300 });
26301  
26302     
26303    
26304    
26305    
26306       
26307 Roo.namespace('Roo.bootstrap.htmleditor');
26308 /**
26309  * @class Roo.bootstrap.HtmlEditorToolbar1
26310  * Basic Toolbar
26311  * 
26312  * @example
26313  * Usage:
26314  *
26315  new Roo.bootstrap.HtmlEditor({
26316     ....
26317     toolbars : [
26318         new Roo.bootstrap.HtmlEditorToolbar1({
26319             disable : { fonts: 1 , format: 1, ..., ... , ...],
26320             btns : [ .... ]
26321         })
26322     }
26323      
26324  * 
26325  * @cfg {Object} disable List of elements to disable..
26326  * @cfg {Array} btns List of additional buttons.
26327  * 
26328  * 
26329  * NEEDS Extra CSS? 
26330  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26331  */
26332  
26333 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26334 {
26335     
26336     Roo.apply(this, config);
26337     
26338     // default disabled, based on 'good practice'..
26339     this.disable = this.disable || {};
26340     Roo.applyIf(this.disable, {
26341         fontSize : true,
26342         colors : true,
26343         specialElements : true
26344     });
26345     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26346     
26347     this.editor = config.editor;
26348     this.editorcore = config.editor.editorcore;
26349     
26350     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26351     
26352     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26353     // dont call parent... till later.
26354 }
26355 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26356      
26357     bar : true,
26358     
26359     editor : false,
26360     editorcore : false,
26361     
26362     
26363     formats : [
26364         "p" ,  
26365         "h1","h2","h3","h4","h5","h6", 
26366         "pre", "code", 
26367         "abbr", "acronym", "address", "cite", "samp", "var",
26368         'div','span'
26369     ],
26370     
26371     onRender : function(ct, position)
26372     {
26373        // Roo.log("Call onRender: " + this.xtype);
26374         
26375        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26376        Roo.log(this.el);
26377        this.el.dom.style.marginBottom = '0';
26378        var _this = this;
26379        var editorcore = this.editorcore;
26380        var editor= this.editor;
26381        
26382        var children = [];
26383        var btn = function(id,cmd , toggle, handler, html){
26384        
26385             var  event = toggle ? 'toggle' : 'click';
26386        
26387             var a = {
26388                 size : 'sm',
26389                 xtype: 'Button',
26390                 xns: Roo.bootstrap,
26391                 //glyphicon : id,
26392                 fa: id,
26393                 cmd : id || cmd,
26394                 enableToggle:toggle !== false,
26395                 html : html || '',
26396                 pressed : toggle ? false : null,
26397                 listeners : {}
26398             };
26399             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26400                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26401             };
26402             children.push(a);
26403             return a;
26404        }
26405        
26406     //    var cb_box = function...
26407         
26408         var style = {
26409                 xtype: 'Button',
26410                 size : 'sm',
26411                 xns: Roo.bootstrap,
26412                 fa : 'font',
26413                 //html : 'submit'
26414                 menu : {
26415                     xtype: 'Menu',
26416                     xns: Roo.bootstrap,
26417                     items:  []
26418                 }
26419         };
26420         Roo.each(this.formats, function(f) {
26421             style.menu.items.push({
26422                 xtype :'MenuItem',
26423                 xns: Roo.bootstrap,
26424                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26425                 tagname : f,
26426                 listeners : {
26427                     click : function()
26428                     {
26429                         editorcore.insertTag(this.tagname);
26430                         editor.focus();
26431                     }
26432                 }
26433                 
26434             });
26435         });
26436         children.push(style);   
26437         
26438         btn('bold',false,true);
26439         btn('italic',false,true);
26440         btn('align-left', 'justifyleft',true);
26441         btn('align-center', 'justifycenter',true);
26442         btn('align-right' , 'justifyright',true);
26443         btn('link', false, false, function(btn) {
26444             //Roo.log("create link?");
26445             var url = prompt(this.createLinkText, this.defaultLinkValue);
26446             if(url && url != 'http:/'+'/'){
26447                 this.editorcore.relayCmd('createlink', url);
26448             }
26449         }),
26450         btn('list','insertunorderedlist',true);
26451         btn('pencil', false,true, function(btn){
26452                 Roo.log(this);
26453                 this.toggleSourceEdit(btn.pressed);
26454         });
26455         
26456         if (this.editor.btns.length > 0) {
26457             for (var i = 0; i<this.editor.btns.length; i++) {
26458                 children.push(this.editor.btns[i]);
26459             }
26460         }
26461         
26462         /*
26463         var cog = {
26464                 xtype: 'Button',
26465                 size : 'sm',
26466                 xns: Roo.bootstrap,
26467                 glyphicon : 'cog',
26468                 //html : 'submit'
26469                 menu : {
26470                     xtype: 'Menu',
26471                     xns: Roo.bootstrap,
26472                     items:  []
26473                 }
26474         };
26475         
26476         cog.menu.items.push({
26477             xtype :'MenuItem',
26478             xns: Roo.bootstrap,
26479             html : Clean styles,
26480             tagname : f,
26481             listeners : {
26482                 click : function()
26483                 {
26484                     editorcore.insertTag(this.tagname);
26485                     editor.focus();
26486                 }
26487             }
26488             
26489         });
26490        */
26491         
26492          
26493        this.xtype = 'NavSimplebar';
26494         
26495         for(var i=0;i< children.length;i++) {
26496             
26497             this.buttons.add(this.addxtypeChild(children[i]));
26498             
26499         }
26500         
26501         editor.on('editorevent', this.updateToolbar, this);
26502     },
26503     onBtnClick : function(id)
26504     {
26505        this.editorcore.relayCmd(id);
26506        this.editorcore.focus();
26507     },
26508     
26509     /**
26510      * Protected method that will not generally be called directly. It triggers
26511      * a toolbar update by reading the markup state of the current selection in the editor.
26512      */
26513     updateToolbar: function(){
26514
26515         if(!this.editorcore.activated){
26516             this.editor.onFirstFocus(); // is this neeed?
26517             return;
26518         }
26519
26520         var btns = this.buttons; 
26521         var doc = this.editorcore.doc;
26522         btns.get('bold').setActive(doc.queryCommandState('bold'));
26523         btns.get('italic').setActive(doc.queryCommandState('italic'));
26524         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26525         
26526         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26527         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26528         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26529         
26530         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26531         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26532          /*
26533         
26534         var ans = this.editorcore.getAllAncestors();
26535         if (this.formatCombo) {
26536             
26537             
26538             var store = this.formatCombo.store;
26539             this.formatCombo.setValue("");
26540             for (var i =0; i < ans.length;i++) {
26541                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26542                     // select it..
26543                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26544                     break;
26545                 }
26546             }
26547         }
26548         
26549         
26550         
26551         // hides menus... - so this cant be on a menu...
26552         Roo.bootstrap.MenuMgr.hideAll();
26553         */
26554         Roo.bootstrap.MenuMgr.hideAll();
26555         //this.editorsyncValue();
26556     },
26557     onFirstFocus: function() {
26558         this.buttons.each(function(item){
26559            item.enable();
26560         });
26561     },
26562     toggleSourceEdit : function(sourceEditMode){
26563         
26564           
26565         if(sourceEditMode){
26566             Roo.log("disabling buttons");
26567            this.buttons.each( function(item){
26568                 if(item.cmd != 'pencil'){
26569                     item.disable();
26570                 }
26571             });
26572           
26573         }else{
26574             Roo.log("enabling buttons");
26575             if(this.editorcore.initialized){
26576                 this.buttons.each( function(item){
26577                     item.enable();
26578                 });
26579             }
26580             
26581         }
26582         Roo.log("calling toggole on editor");
26583         // tell the editor that it's been pressed..
26584         this.editor.toggleSourceEdit(sourceEditMode);
26585        
26586     }
26587 });
26588
26589
26590
26591
26592  
26593 /*
26594  * - LGPL
26595  */
26596
26597 /**
26598  * @class Roo.bootstrap.Markdown
26599  * @extends Roo.bootstrap.TextArea
26600  * Bootstrap Showdown editable area
26601  * @cfg {string} content
26602  * 
26603  * @constructor
26604  * Create a new Showdown
26605  */
26606
26607 Roo.bootstrap.Markdown = function(config){
26608     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26609    
26610 };
26611
26612 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26613     
26614     editing :false,
26615     
26616     initEvents : function()
26617     {
26618         
26619         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26620         this.markdownEl = this.el.createChild({
26621             cls : 'roo-markdown-area'
26622         });
26623         this.inputEl().addClass('d-none');
26624         if (this.getValue() == '') {
26625             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26626             
26627         } else {
26628             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26629         }
26630         this.markdownEl.on('click', this.toggleTextEdit, this);
26631         this.on('blur', this.toggleTextEdit, this);
26632         this.on('specialkey', this.resizeTextArea, this);
26633     },
26634     
26635     toggleTextEdit : function()
26636     {
26637         var sh = this.markdownEl.getHeight();
26638         this.inputEl().addClass('d-none');
26639         this.markdownEl.addClass('d-none');
26640         if (!this.editing) {
26641             // show editor?
26642             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26643             this.inputEl().removeClass('d-none');
26644             this.inputEl().focus();
26645             this.editing = true;
26646             return;
26647         }
26648         // show showdown...
26649         this.updateMarkdown();
26650         this.markdownEl.removeClass('d-none');
26651         this.editing = false;
26652         return;
26653     },
26654     updateMarkdown : function()
26655     {
26656         if (this.getValue() == '') {
26657             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26658             return;
26659         }
26660  
26661         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26662     },
26663     
26664     resizeTextArea: function () {
26665         
26666         var sh = 100;
26667         Roo.log([sh, this.getValue().split("\n").length * 30]);
26668         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26669     },
26670     setValue : function(val)
26671     {
26672         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26673         if (!this.editing) {
26674             this.updateMarkdown();
26675         }
26676         
26677     },
26678     focus : function()
26679     {
26680         if (!this.editing) {
26681             this.toggleTextEdit();
26682         }
26683         
26684     }
26685
26686
26687 });
26688 /**
26689  * @class Roo.bootstrap.Table.AbstractSelectionModel
26690  * @extends Roo.util.Observable
26691  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26692  * implemented by descendant classes.  This class should not be directly instantiated.
26693  * @constructor
26694  */
26695 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26696     this.locked = false;
26697     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26698 };
26699
26700
26701 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26702     /** @ignore Called by the grid automatically. Do not call directly. */
26703     init : function(grid){
26704         this.grid = grid;
26705         this.initEvents();
26706     },
26707
26708     /**
26709      * Locks the selections.
26710      */
26711     lock : function(){
26712         this.locked = true;
26713     },
26714
26715     /**
26716      * Unlocks the selections.
26717      */
26718     unlock : function(){
26719         this.locked = false;
26720     },
26721
26722     /**
26723      * Returns true if the selections are locked.
26724      * @return {Boolean}
26725      */
26726     isLocked : function(){
26727         return this.locked;
26728     },
26729     
26730     
26731     initEvents : function ()
26732     {
26733         
26734     }
26735 });
26736 /**
26737  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26738  * @class Roo.bootstrap.Table.RowSelectionModel
26739  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26740  * It supports multiple selections and keyboard selection/navigation. 
26741  * @constructor
26742  * @param {Object} config
26743  */
26744
26745 Roo.bootstrap.Table.RowSelectionModel = function(config){
26746     Roo.apply(this, config);
26747     this.selections = new Roo.util.MixedCollection(false, function(o){
26748         return o.id;
26749     });
26750
26751     this.last = false;
26752     this.lastActive = false;
26753
26754     this.addEvents({
26755         /**
26756              * @event selectionchange
26757              * Fires when the selection changes
26758              * @param {SelectionModel} this
26759              */
26760             "selectionchange" : true,
26761         /**
26762              * @event afterselectionchange
26763              * Fires after the selection changes (eg. by key press or clicking)
26764              * @param {SelectionModel} this
26765              */
26766             "afterselectionchange" : true,
26767         /**
26768              * @event beforerowselect
26769              * Fires when a row is selected being selected, return false to cancel.
26770              * @param {SelectionModel} this
26771              * @param {Number} rowIndex The selected index
26772              * @param {Boolean} keepExisting False if other selections will be cleared
26773              */
26774             "beforerowselect" : true,
26775         /**
26776              * @event rowselect
26777              * Fires when a row is selected.
26778              * @param {SelectionModel} this
26779              * @param {Number} rowIndex The selected index
26780              * @param {Roo.data.Record} r The record
26781              */
26782             "rowselect" : true,
26783         /**
26784              * @event rowdeselect
26785              * Fires when a row is deselected.
26786              * @param {SelectionModel} this
26787              * @param {Number} rowIndex The selected index
26788              */
26789         "rowdeselect" : true
26790     });
26791     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26792     this.locked = false;
26793  };
26794
26795 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26796     /**
26797      * @cfg {Boolean} singleSelect
26798      * True to allow selection of only one row at a time (defaults to false)
26799      */
26800     singleSelect : false,
26801
26802     // private
26803     initEvents : function()
26804     {
26805
26806         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26807         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26808         //}else{ // allow click to work like normal
26809          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26810         //}
26811         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26812         this.grid.on("rowclick", this.handleMouseDown, this);
26813         
26814         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26815             "up" : function(e){
26816                 if(!e.shiftKey){
26817                     this.selectPrevious(e.shiftKey);
26818                 }else if(this.last !== false && this.lastActive !== false){
26819                     var last = this.last;
26820                     this.selectRange(this.last,  this.lastActive-1);
26821                     this.grid.getView().focusRow(this.lastActive);
26822                     if(last !== false){
26823                         this.last = last;
26824                     }
26825                 }else{
26826                     this.selectFirstRow();
26827                 }
26828                 this.fireEvent("afterselectionchange", this);
26829             },
26830             "down" : function(e){
26831                 if(!e.shiftKey){
26832                     this.selectNext(e.shiftKey);
26833                 }else if(this.last !== false && this.lastActive !== false){
26834                     var last = this.last;
26835                     this.selectRange(this.last,  this.lastActive+1);
26836                     this.grid.getView().focusRow(this.lastActive);
26837                     if(last !== false){
26838                         this.last = last;
26839                     }
26840                 }else{
26841                     this.selectFirstRow();
26842                 }
26843                 this.fireEvent("afterselectionchange", this);
26844             },
26845             scope: this
26846         });
26847         this.grid.store.on('load', function(){
26848             this.selections.clear();
26849         },this);
26850         /*
26851         var view = this.grid.view;
26852         view.on("refresh", this.onRefresh, this);
26853         view.on("rowupdated", this.onRowUpdated, this);
26854         view.on("rowremoved", this.onRemove, this);
26855         */
26856     },
26857
26858     // private
26859     onRefresh : function()
26860     {
26861         var ds = this.grid.store, i, v = this.grid.view;
26862         var s = this.selections;
26863         s.each(function(r){
26864             if((i = ds.indexOfId(r.id)) != -1){
26865                 v.onRowSelect(i);
26866             }else{
26867                 s.remove(r);
26868             }
26869         });
26870     },
26871
26872     // private
26873     onRemove : function(v, index, r){
26874         this.selections.remove(r);
26875     },
26876
26877     // private
26878     onRowUpdated : function(v, index, r){
26879         if(this.isSelected(r)){
26880             v.onRowSelect(index);
26881         }
26882     },
26883
26884     /**
26885      * Select records.
26886      * @param {Array} records The records to select
26887      * @param {Boolean} keepExisting (optional) True to keep existing selections
26888      */
26889     selectRecords : function(records, keepExisting)
26890     {
26891         if(!keepExisting){
26892             this.clearSelections();
26893         }
26894             var ds = this.grid.store;
26895         for(var i = 0, len = records.length; i < len; i++){
26896             this.selectRow(ds.indexOf(records[i]), true);
26897         }
26898     },
26899
26900     /**
26901      * Gets the number of selected rows.
26902      * @return {Number}
26903      */
26904     getCount : function(){
26905         return this.selections.length;
26906     },
26907
26908     /**
26909      * Selects the first row in the grid.
26910      */
26911     selectFirstRow : function(){
26912         this.selectRow(0);
26913     },
26914
26915     /**
26916      * Select the last row.
26917      * @param {Boolean} keepExisting (optional) True to keep existing selections
26918      */
26919     selectLastRow : function(keepExisting){
26920         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26921         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26922     },
26923
26924     /**
26925      * Selects the row immediately following the last selected row.
26926      * @param {Boolean} keepExisting (optional) True to keep existing selections
26927      */
26928     selectNext : function(keepExisting)
26929     {
26930             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26931             this.selectRow(this.last+1, keepExisting);
26932             this.grid.getView().focusRow(this.last);
26933         }
26934     },
26935
26936     /**
26937      * Selects the row that precedes the last selected row.
26938      * @param {Boolean} keepExisting (optional) True to keep existing selections
26939      */
26940     selectPrevious : function(keepExisting){
26941         if(this.last){
26942             this.selectRow(this.last-1, keepExisting);
26943             this.grid.getView().focusRow(this.last);
26944         }
26945     },
26946
26947     /**
26948      * Returns the selected records
26949      * @return {Array} Array of selected records
26950      */
26951     getSelections : function(){
26952         return [].concat(this.selections.items);
26953     },
26954
26955     /**
26956      * Returns the first selected record.
26957      * @return {Record}
26958      */
26959     getSelected : function(){
26960         return this.selections.itemAt(0);
26961     },
26962
26963
26964     /**
26965      * Clears all selections.
26966      */
26967     clearSelections : function(fast)
26968     {
26969         if(this.locked) {
26970             return;
26971         }
26972         if(fast !== true){
26973                 var ds = this.grid.store;
26974             var s = this.selections;
26975             s.each(function(r){
26976                 this.deselectRow(ds.indexOfId(r.id));
26977             }, this);
26978             s.clear();
26979         }else{
26980             this.selections.clear();
26981         }
26982         this.last = false;
26983     },
26984
26985
26986     /**
26987      * Selects all rows.
26988      */
26989     selectAll : function(){
26990         if(this.locked) {
26991             return;
26992         }
26993         this.selections.clear();
26994         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26995             this.selectRow(i, true);
26996         }
26997     },
26998
26999     /**
27000      * Returns True if there is a selection.
27001      * @return {Boolean}
27002      */
27003     hasSelection : function(){
27004         return this.selections.length > 0;
27005     },
27006
27007     /**
27008      * Returns True if the specified row is selected.
27009      * @param {Number/Record} record The record or index of the record to check
27010      * @return {Boolean}
27011      */
27012     isSelected : function(index){
27013             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27014         return (r && this.selections.key(r.id) ? true : false);
27015     },
27016
27017     /**
27018      * Returns True if the specified record id is selected.
27019      * @param {String} id The id of record to check
27020      * @return {Boolean}
27021      */
27022     isIdSelected : function(id){
27023         return (this.selections.key(id) ? true : false);
27024     },
27025
27026
27027     // private
27028     handleMouseDBClick : function(e, t){
27029         
27030     },
27031     // private
27032     handleMouseDown : function(e, t)
27033     {
27034             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27035         if(this.isLocked() || rowIndex < 0 ){
27036             return;
27037         };
27038         if(e.shiftKey && this.last !== false){
27039             var last = this.last;
27040             this.selectRange(last, rowIndex, e.ctrlKey);
27041             this.last = last; // reset the last
27042             t.focus();
27043     
27044         }else{
27045             var isSelected = this.isSelected(rowIndex);
27046             //Roo.log("select row:" + rowIndex);
27047             if(isSelected){
27048                 this.deselectRow(rowIndex);
27049             } else {
27050                         this.selectRow(rowIndex, true);
27051             }
27052     
27053             /*
27054                 if(e.button !== 0 && isSelected){
27055                 alert('rowIndex 2: ' + rowIndex);
27056                     view.focusRow(rowIndex);
27057                 }else if(e.ctrlKey && isSelected){
27058                     this.deselectRow(rowIndex);
27059                 }else if(!isSelected){
27060                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27061                     view.focusRow(rowIndex);
27062                 }
27063             */
27064         }
27065         this.fireEvent("afterselectionchange", this);
27066     },
27067     // private
27068     handleDragableRowClick :  function(grid, rowIndex, e) 
27069     {
27070         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27071             this.selectRow(rowIndex, false);
27072             grid.view.focusRow(rowIndex);
27073              this.fireEvent("afterselectionchange", this);
27074         }
27075     },
27076     
27077     /**
27078      * Selects multiple rows.
27079      * @param {Array} rows Array of the indexes of the row to select
27080      * @param {Boolean} keepExisting (optional) True to keep existing selections
27081      */
27082     selectRows : function(rows, keepExisting){
27083         if(!keepExisting){
27084             this.clearSelections();
27085         }
27086         for(var i = 0, len = rows.length; i < len; i++){
27087             this.selectRow(rows[i], true);
27088         }
27089     },
27090
27091     /**
27092      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27093      * @param {Number} startRow The index of the first row in the range
27094      * @param {Number} endRow The index of the last row in the range
27095      * @param {Boolean} keepExisting (optional) True to retain existing selections
27096      */
27097     selectRange : function(startRow, endRow, keepExisting){
27098         if(this.locked) {
27099             return;
27100         }
27101         if(!keepExisting){
27102             this.clearSelections();
27103         }
27104         if(startRow <= endRow){
27105             for(var i = startRow; i <= endRow; i++){
27106                 this.selectRow(i, true);
27107             }
27108         }else{
27109             for(var i = startRow; i >= endRow; i--){
27110                 this.selectRow(i, true);
27111             }
27112         }
27113     },
27114
27115     /**
27116      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27117      * @param {Number} startRow The index of the first row in the range
27118      * @param {Number} endRow The index of the last row in the range
27119      */
27120     deselectRange : function(startRow, endRow, preventViewNotify){
27121         if(this.locked) {
27122             return;
27123         }
27124         for(var i = startRow; i <= endRow; i++){
27125             this.deselectRow(i, preventViewNotify);
27126         }
27127     },
27128
27129     /**
27130      * Selects a row.
27131      * @param {Number} row The index of the row to select
27132      * @param {Boolean} keepExisting (optional) True to keep existing selections
27133      */
27134     selectRow : function(index, keepExisting, preventViewNotify)
27135     {
27136             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27137             return;
27138         }
27139         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27140             if(!keepExisting || this.singleSelect){
27141                 this.clearSelections();
27142             }
27143             
27144             var r = this.grid.store.getAt(index);
27145             //console.log('selectRow - record id :' + r.id);
27146             
27147             this.selections.add(r);
27148             this.last = this.lastActive = index;
27149             if(!preventViewNotify){
27150                 var proxy = new Roo.Element(
27151                                 this.grid.getRowDom(index)
27152                 );
27153                 proxy.addClass('bg-info info');
27154             }
27155             this.fireEvent("rowselect", this, index, r);
27156             this.fireEvent("selectionchange", this);
27157         }
27158     },
27159
27160     /**
27161      * Deselects a row.
27162      * @param {Number} row The index of the row to deselect
27163      */
27164     deselectRow : function(index, preventViewNotify)
27165     {
27166         if(this.locked) {
27167             return;
27168         }
27169         if(this.last == index){
27170             this.last = false;
27171         }
27172         if(this.lastActive == index){
27173             this.lastActive = false;
27174         }
27175         
27176         var r = this.grid.store.getAt(index);
27177         if (!r) {
27178             return;
27179         }
27180         
27181         this.selections.remove(r);
27182         //.console.log('deselectRow - record id :' + r.id);
27183         if(!preventViewNotify){
27184         
27185             var proxy = new Roo.Element(
27186                 this.grid.getRowDom(index)
27187             );
27188             proxy.removeClass('bg-info info');
27189         }
27190         this.fireEvent("rowdeselect", this, index);
27191         this.fireEvent("selectionchange", this);
27192     },
27193
27194     // private
27195     restoreLast : function(){
27196         if(this._last){
27197             this.last = this._last;
27198         }
27199     },
27200
27201     // private
27202     acceptsNav : function(row, col, cm){
27203         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27204     },
27205
27206     // private
27207     onEditorKey : function(field, e){
27208         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27209         if(k == e.TAB){
27210             e.stopEvent();
27211             ed.completeEdit();
27212             if(e.shiftKey){
27213                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27214             }else{
27215                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27216             }
27217         }else if(k == e.ENTER && !e.ctrlKey){
27218             e.stopEvent();
27219             ed.completeEdit();
27220             if(e.shiftKey){
27221                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27222             }else{
27223                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27224             }
27225         }else if(k == e.ESC){
27226             ed.cancelEdit();
27227         }
27228         if(newCell){
27229             g.startEditing(newCell[0], newCell[1]);
27230         }
27231     }
27232 });
27233 /*
27234  * Based on:
27235  * Ext JS Library 1.1.1
27236  * Copyright(c) 2006-2007, Ext JS, LLC.
27237  *
27238  * Originally Released Under LGPL - original licence link has changed is not relivant.
27239  *
27240  * Fork - LGPL
27241  * <script type="text/javascript">
27242  */
27243  
27244 /**
27245  * @class Roo.bootstrap.PagingToolbar
27246  * @extends Roo.bootstrap.NavSimplebar
27247  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27248  * @constructor
27249  * Create a new PagingToolbar
27250  * @param {Object} config The config object
27251  * @param {Roo.data.Store} store
27252  */
27253 Roo.bootstrap.PagingToolbar = function(config)
27254 {
27255     // old args format still supported... - xtype is prefered..
27256         // created from xtype...
27257     
27258     this.ds = config.dataSource;
27259     
27260     if (config.store && !this.ds) {
27261         this.store= Roo.factory(config.store, Roo.data);
27262         this.ds = this.store;
27263         this.ds.xmodule = this.xmodule || false;
27264     }
27265     
27266     this.toolbarItems = [];
27267     if (config.items) {
27268         this.toolbarItems = config.items;
27269     }
27270     
27271     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27272     
27273     this.cursor = 0;
27274     
27275     if (this.ds) { 
27276         this.bind(this.ds);
27277     }
27278     
27279     if (Roo.bootstrap.version == 4) {
27280         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27281     } else {
27282         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27283     }
27284     
27285 };
27286
27287 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27288     /**
27289      * @cfg {Roo.data.Store} dataSource
27290      * The underlying data store providing the paged data
27291      */
27292     /**
27293      * @cfg {String/HTMLElement/Element} container
27294      * container The id or element that will contain the toolbar
27295      */
27296     /**
27297      * @cfg {Boolean} displayInfo
27298      * True to display the displayMsg (defaults to false)
27299      */
27300     /**
27301      * @cfg {Number} pageSize
27302      * The number of records to display per page (defaults to 20)
27303      */
27304     pageSize: 20,
27305     /**
27306      * @cfg {String} displayMsg
27307      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27308      */
27309     displayMsg : 'Displaying {0} - {1} of {2}',
27310     /**
27311      * @cfg {String} emptyMsg
27312      * The message to display when no records are found (defaults to "No data to display")
27313      */
27314     emptyMsg : 'No data to display',
27315     /**
27316      * Customizable piece of the default paging text (defaults to "Page")
27317      * @type String
27318      */
27319     beforePageText : "Page",
27320     /**
27321      * Customizable piece of the default paging text (defaults to "of %0")
27322      * @type String
27323      */
27324     afterPageText : "of {0}",
27325     /**
27326      * Customizable piece of the default paging text (defaults to "First Page")
27327      * @type String
27328      */
27329     firstText : "First Page",
27330     /**
27331      * Customizable piece of the default paging text (defaults to "Previous Page")
27332      * @type String
27333      */
27334     prevText : "Previous Page",
27335     /**
27336      * Customizable piece of the default paging text (defaults to "Next Page")
27337      * @type String
27338      */
27339     nextText : "Next Page",
27340     /**
27341      * Customizable piece of the default paging text (defaults to "Last Page")
27342      * @type String
27343      */
27344     lastText : "Last Page",
27345     /**
27346      * Customizable piece of the default paging text (defaults to "Refresh")
27347      * @type String
27348      */
27349     refreshText : "Refresh",
27350
27351     buttons : false,
27352     // private
27353     onRender : function(ct, position) 
27354     {
27355         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27356         this.navgroup.parentId = this.id;
27357         this.navgroup.onRender(this.el, null);
27358         // add the buttons to the navgroup
27359         
27360         if(this.displayInfo){
27361             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27362             this.displayEl = this.el.select('.x-paging-info', true).first();
27363 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27364 //            this.displayEl = navel.el.select('span',true).first();
27365         }
27366         
27367         var _this = this;
27368         
27369         if(this.buttons){
27370             Roo.each(_this.buttons, function(e){ // this might need to use render????
27371                Roo.factory(e).render(_this.el);
27372             });
27373         }
27374             
27375         Roo.each(_this.toolbarItems, function(e) {
27376             _this.navgroup.addItem(e);
27377         });
27378         
27379         
27380         this.first = this.navgroup.addItem({
27381             tooltip: this.firstText,
27382             cls: "prev btn-outline-secondary",
27383             html : ' <i class="fa fa-step-backward"></i>',
27384             disabled: true,
27385             preventDefault: true,
27386             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27387         });
27388         
27389         this.prev =  this.navgroup.addItem({
27390             tooltip: this.prevText,
27391             cls: "prev btn-outline-secondary",
27392             html : ' <i class="fa fa-backward"></i>',
27393             disabled: true,
27394             preventDefault: true,
27395             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27396         });
27397     //this.addSeparator();
27398         
27399         
27400         var field = this.navgroup.addItem( {
27401             tagtype : 'span',
27402             cls : 'x-paging-position  btn-outline-secondary',
27403              disabled: true,
27404             html : this.beforePageText  +
27405                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27406                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27407          } ); //?? escaped?
27408         
27409         this.field = field.el.select('input', true).first();
27410         this.field.on("keydown", this.onPagingKeydown, this);
27411         this.field.on("focus", function(){this.dom.select();});
27412     
27413     
27414         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27415         //this.field.setHeight(18);
27416         //this.addSeparator();
27417         this.next = this.navgroup.addItem({
27418             tooltip: this.nextText,
27419             cls: "next btn-outline-secondary",
27420             html : ' <i class="fa fa-forward"></i>',
27421             disabled: true,
27422             preventDefault: true,
27423             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27424         });
27425         this.last = this.navgroup.addItem({
27426             tooltip: this.lastText,
27427             html : ' <i class="fa fa-step-forward"></i>',
27428             cls: "next btn-outline-secondary",
27429             disabled: true,
27430             preventDefault: true,
27431             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27432         });
27433     //this.addSeparator();
27434         this.loading = this.navgroup.addItem({
27435             tooltip: this.refreshText,
27436             cls: "btn-outline-secondary",
27437             html : ' <i class="fa fa-refresh"></i>',
27438             preventDefault: true,
27439             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27440         });
27441         
27442     },
27443
27444     // private
27445     updateInfo : function(){
27446         if(this.displayEl){
27447             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27448             var msg = count == 0 ?
27449                 this.emptyMsg :
27450                 String.format(
27451                     this.displayMsg,
27452                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27453                 );
27454             this.displayEl.update(msg);
27455         }
27456     },
27457
27458     // private
27459     onLoad : function(ds, r, o)
27460     {
27461         this.cursor = o.params && o.params.start ? o.params.start : 0;
27462         
27463         var d = this.getPageData(),
27464             ap = d.activePage,
27465             ps = d.pages;
27466         
27467         
27468         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27469         this.field.dom.value = ap;
27470         this.first.setDisabled(ap == 1);
27471         this.prev.setDisabled(ap == 1);
27472         this.next.setDisabled(ap == ps);
27473         this.last.setDisabled(ap == ps);
27474         this.loading.enable();
27475         this.updateInfo();
27476     },
27477
27478     // private
27479     getPageData : function(){
27480         var total = this.ds.getTotalCount();
27481         return {
27482             total : total,
27483             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27484             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27485         };
27486     },
27487
27488     // private
27489     onLoadError : function(){
27490         this.loading.enable();
27491     },
27492
27493     // private
27494     onPagingKeydown : function(e){
27495         var k = e.getKey();
27496         var d = this.getPageData();
27497         if(k == e.RETURN){
27498             var v = this.field.dom.value, pageNum;
27499             if(!v || isNaN(pageNum = parseInt(v, 10))){
27500                 this.field.dom.value = d.activePage;
27501                 return;
27502             }
27503             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27504             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27505             e.stopEvent();
27506         }
27507         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))
27508         {
27509           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27510           this.field.dom.value = pageNum;
27511           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27512           e.stopEvent();
27513         }
27514         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27515         {
27516           var v = this.field.dom.value, pageNum; 
27517           var increment = (e.shiftKey) ? 10 : 1;
27518           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27519                 increment *= -1;
27520           }
27521           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27522             this.field.dom.value = d.activePage;
27523             return;
27524           }
27525           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27526           {
27527             this.field.dom.value = parseInt(v, 10) + increment;
27528             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27529             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27530           }
27531           e.stopEvent();
27532         }
27533     },
27534
27535     // private
27536     beforeLoad : function(){
27537         if(this.loading){
27538             this.loading.disable();
27539         }
27540     },
27541
27542     // private
27543     onClick : function(which){
27544         
27545         var ds = this.ds;
27546         if (!ds) {
27547             return;
27548         }
27549         
27550         switch(which){
27551             case "first":
27552                 ds.load({params:{start: 0, limit: this.pageSize}});
27553             break;
27554             case "prev":
27555                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27556             break;
27557             case "next":
27558                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27559             break;
27560             case "last":
27561                 var total = ds.getTotalCount();
27562                 var extra = total % this.pageSize;
27563                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27564                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27565             break;
27566             case "refresh":
27567                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27568             break;
27569         }
27570     },
27571
27572     /**
27573      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27574      * @param {Roo.data.Store} store The data store to unbind
27575      */
27576     unbind : function(ds){
27577         ds.un("beforeload", this.beforeLoad, this);
27578         ds.un("load", this.onLoad, this);
27579         ds.un("loadexception", this.onLoadError, this);
27580         ds.un("remove", this.updateInfo, this);
27581         ds.un("add", this.updateInfo, this);
27582         this.ds = undefined;
27583     },
27584
27585     /**
27586      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27587      * @param {Roo.data.Store} store The data store to bind
27588      */
27589     bind : function(ds){
27590         ds.on("beforeload", this.beforeLoad, this);
27591         ds.on("load", this.onLoad, this);
27592         ds.on("loadexception", this.onLoadError, this);
27593         ds.on("remove", this.updateInfo, this);
27594         ds.on("add", this.updateInfo, this);
27595         this.ds = ds;
27596     }
27597 });/*
27598  * - LGPL
27599  *
27600  * element
27601  * 
27602  */
27603
27604 /**
27605  * @class Roo.bootstrap.MessageBar
27606  * @extends Roo.bootstrap.Component
27607  * Bootstrap MessageBar class
27608  * @cfg {String} html contents of the MessageBar
27609  * @cfg {String} weight (info | success | warning | danger) default info
27610  * @cfg {String} beforeClass insert the bar before the given class
27611  * @cfg {Boolean} closable (true | false) default false
27612  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27613  * 
27614  * @constructor
27615  * Create a new Element
27616  * @param {Object} config The config object
27617  */
27618
27619 Roo.bootstrap.MessageBar = function(config){
27620     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27621 };
27622
27623 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27624     
27625     html: '',
27626     weight: 'info',
27627     closable: false,
27628     fixed: false,
27629     beforeClass: 'bootstrap-sticky-wrap',
27630     
27631     getAutoCreate : function(){
27632         
27633         var cfg = {
27634             tag: 'div',
27635             cls: 'alert alert-dismissable alert-' + this.weight,
27636             cn: [
27637                 {
27638                     tag: 'span',
27639                     cls: 'message',
27640                     html: this.html || ''
27641                 }
27642             ]
27643         };
27644         
27645         if(this.fixed){
27646             cfg.cls += ' alert-messages-fixed';
27647         }
27648         
27649         if(this.closable){
27650             cfg.cn.push({
27651                 tag: 'button',
27652                 cls: 'close',
27653                 html: 'x'
27654             });
27655         }
27656         
27657         return cfg;
27658     },
27659     
27660     onRender : function(ct, position)
27661     {
27662         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27663         
27664         if(!this.el){
27665             var cfg = Roo.apply({},  this.getAutoCreate());
27666             cfg.id = Roo.id();
27667             
27668             if (this.cls) {
27669                 cfg.cls += ' ' + this.cls;
27670             }
27671             if (this.style) {
27672                 cfg.style = this.style;
27673             }
27674             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27675             
27676             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27677         }
27678         
27679         this.el.select('>button.close').on('click', this.hide, this);
27680         
27681     },
27682     
27683     show : function()
27684     {
27685         if (!this.rendered) {
27686             this.render();
27687         }
27688         
27689         this.el.show();
27690         
27691         this.fireEvent('show', this);
27692         
27693     },
27694     
27695     hide : function()
27696     {
27697         if (!this.rendered) {
27698             this.render();
27699         }
27700         
27701         this.el.hide();
27702         
27703         this.fireEvent('hide', this);
27704     },
27705     
27706     update : function()
27707     {
27708 //        var e = this.el.dom.firstChild;
27709 //        
27710 //        if(this.closable){
27711 //            e = e.nextSibling;
27712 //        }
27713 //        
27714 //        e.data = this.html || '';
27715
27716         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27717     }
27718    
27719 });
27720
27721  
27722
27723      /*
27724  * - LGPL
27725  *
27726  * Graph
27727  * 
27728  */
27729
27730
27731 /**
27732  * @class Roo.bootstrap.Graph
27733  * @extends Roo.bootstrap.Component
27734  * Bootstrap Graph class
27735 > Prameters
27736  -sm {number} sm 4
27737  -md {number} md 5
27738  @cfg {String} graphtype  bar | vbar | pie
27739  @cfg {number} g_x coodinator | centre x (pie)
27740  @cfg {number} g_y coodinator | centre y (pie)
27741  @cfg {number} g_r radius (pie)
27742  @cfg {number} g_height height of the chart (respected by all elements in the set)
27743  @cfg {number} g_width width of the chart (respected by all elements in the set)
27744  @cfg {Object} title The title of the chart
27745     
27746  -{Array}  values
27747  -opts (object) options for the chart 
27748      o {
27749      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27750      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27751      o vgutter (number)
27752      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.
27753      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27754      o to
27755      o stretch (boolean)
27756      o }
27757  -opts (object) options for the pie
27758      o{
27759      o cut
27760      o startAngle (number)
27761      o endAngle (number)
27762      } 
27763  *
27764  * @constructor
27765  * Create a new Input
27766  * @param {Object} config The config object
27767  */
27768
27769 Roo.bootstrap.Graph = function(config){
27770     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27771     
27772     this.addEvents({
27773         // img events
27774         /**
27775          * @event click
27776          * The img click event for the img.
27777          * @param {Roo.EventObject} e
27778          */
27779         "click" : true
27780     });
27781 };
27782
27783 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27784     
27785     sm: 4,
27786     md: 5,
27787     graphtype: 'bar',
27788     g_height: 250,
27789     g_width: 400,
27790     g_x: 50,
27791     g_y: 50,
27792     g_r: 30,
27793     opts:{
27794         //g_colors: this.colors,
27795         g_type: 'soft',
27796         g_gutter: '20%'
27797
27798     },
27799     title : false,
27800
27801     getAutoCreate : function(){
27802         
27803         var cfg = {
27804             tag: 'div',
27805             html : null
27806         };
27807         
27808         
27809         return  cfg;
27810     },
27811
27812     onRender : function(ct,position){
27813         
27814         
27815         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27816         
27817         if (typeof(Raphael) == 'undefined') {
27818             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27819             return;
27820         }
27821         
27822         this.raphael = Raphael(this.el.dom);
27823         
27824                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27825                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27826                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27827                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27828                 /*
27829                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27830                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27831                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27832                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27833                 
27834                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27835                 r.barchart(330, 10, 300, 220, data1);
27836                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27837                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27838                 */
27839                 
27840                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27841                 // r.barchart(30, 30, 560, 250,  xdata, {
27842                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27843                 //     axis : "0 0 1 1",
27844                 //     axisxlabels :  xdata
27845                 //     //yvalues : cols,
27846                    
27847                 // });
27848 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27849 //        
27850 //        this.load(null,xdata,{
27851 //                axis : "0 0 1 1",
27852 //                axisxlabels :  xdata
27853 //                });
27854
27855     },
27856
27857     load : function(graphtype,xdata,opts)
27858     {
27859         this.raphael.clear();
27860         if(!graphtype) {
27861             graphtype = this.graphtype;
27862         }
27863         if(!opts){
27864             opts = this.opts;
27865         }
27866         var r = this.raphael,
27867             fin = function () {
27868                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27869             },
27870             fout = function () {
27871                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27872             },
27873             pfin = function() {
27874                 this.sector.stop();
27875                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27876
27877                 if (this.label) {
27878                     this.label[0].stop();
27879                     this.label[0].attr({ r: 7.5 });
27880                     this.label[1].attr({ "font-weight": 800 });
27881                 }
27882             },
27883             pfout = function() {
27884                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27885
27886                 if (this.label) {
27887                     this.label[0].animate({ r: 5 }, 500, "bounce");
27888                     this.label[1].attr({ "font-weight": 400 });
27889                 }
27890             };
27891
27892         switch(graphtype){
27893             case 'bar':
27894                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27895                 break;
27896             case 'hbar':
27897                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27898                 break;
27899             case 'pie':
27900 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27901 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27902 //            
27903                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27904                 
27905                 break;
27906
27907         }
27908         
27909         if(this.title){
27910             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27911         }
27912         
27913     },
27914     
27915     setTitle: function(o)
27916     {
27917         this.title = o;
27918     },
27919     
27920     initEvents: function() {
27921         
27922         if(!this.href){
27923             this.el.on('click', this.onClick, this);
27924         }
27925     },
27926     
27927     onClick : function(e)
27928     {
27929         Roo.log('img onclick');
27930         this.fireEvent('click', this, e);
27931     }
27932    
27933 });
27934
27935  
27936 /*
27937  * - LGPL
27938  *
27939  * numberBox
27940  * 
27941  */
27942 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27943
27944 /**
27945  * @class Roo.bootstrap.dash.NumberBox
27946  * @extends Roo.bootstrap.Component
27947  * Bootstrap NumberBox class
27948  * @cfg {String} headline Box headline
27949  * @cfg {String} content Box content
27950  * @cfg {String} icon Box icon
27951  * @cfg {String} footer Footer text
27952  * @cfg {String} fhref Footer href
27953  * 
27954  * @constructor
27955  * Create a new NumberBox
27956  * @param {Object} config The config object
27957  */
27958
27959
27960 Roo.bootstrap.dash.NumberBox = function(config){
27961     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27962     
27963 };
27964
27965 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27966     
27967     headline : '',
27968     content : '',
27969     icon : '',
27970     footer : '',
27971     fhref : '',
27972     ficon : '',
27973     
27974     getAutoCreate : function(){
27975         
27976         var cfg = {
27977             tag : 'div',
27978             cls : 'small-box ',
27979             cn : [
27980                 {
27981                     tag : 'div',
27982                     cls : 'inner',
27983                     cn :[
27984                         {
27985                             tag : 'h3',
27986                             cls : 'roo-headline',
27987                             html : this.headline
27988                         },
27989                         {
27990                             tag : 'p',
27991                             cls : 'roo-content',
27992                             html : this.content
27993                         }
27994                     ]
27995                 }
27996             ]
27997         };
27998         
27999         if(this.icon){
28000             cfg.cn.push({
28001                 tag : 'div',
28002                 cls : 'icon',
28003                 cn :[
28004                     {
28005                         tag : 'i',
28006                         cls : 'ion ' + this.icon
28007                     }
28008                 ]
28009             });
28010         }
28011         
28012         if(this.footer){
28013             var footer = {
28014                 tag : 'a',
28015                 cls : 'small-box-footer',
28016                 href : this.fhref || '#',
28017                 html : this.footer
28018             };
28019             
28020             cfg.cn.push(footer);
28021             
28022         }
28023         
28024         return  cfg;
28025     },
28026
28027     onRender : function(ct,position){
28028         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28029
28030
28031        
28032                 
28033     },
28034
28035     setHeadline: function (value)
28036     {
28037         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28038     },
28039     
28040     setFooter: function (value, href)
28041     {
28042         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28043         
28044         if(href){
28045             this.el.select('a.small-box-footer',true).first().attr('href', href);
28046         }
28047         
28048     },
28049
28050     setContent: function (value)
28051     {
28052         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28053     },
28054
28055     initEvents: function() 
28056     {   
28057         
28058     }
28059     
28060 });
28061
28062  
28063 /*
28064  * - LGPL
28065  *
28066  * TabBox
28067  * 
28068  */
28069 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28070
28071 /**
28072  * @class Roo.bootstrap.dash.TabBox
28073  * @extends Roo.bootstrap.Component
28074  * Bootstrap TabBox class
28075  * @cfg {String} title Title of the TabBox
28076  * @cfg {String} icon Icon of the TabBox
28077  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28078  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28079  * 
28080  * @constructor
28081  * Create a new TabBox
28082  * @param {Object} config The config object
28083  */
28084
28085
28086 Roo.bootstrap.dash.TabBox = function(config){
28087     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28088     this.addEvents({
28089         // raw events
28090         /**
28091          * @event addpane
28092          * When a pane is added
28093          * @param {Roo.bootstrap.dash.TabPane} pane
28094          */
28095         "addpane" : true,
28096         /**
28097          * @event activatepane
28098          * When a pane is activated
28099          * @param {Roo.bootstrap.dash.TabPane} pane
28100          */
28101         "activatepane" : true
28102         
28103          
28104     });
28105     
28106     this.panes = [];
28107 };
28108
28109 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28110
28111     title : '',
28112     icon : false,
28113     showtabs : true,
28114     tabScrollable : false,
28115     
28116     getChildContainer : function()
28117     {
28118         return this.el.select('.tab-content', true).first();
28119     },
28120     
28121     getAutoCreate : function(){
28122         
28123         var header = {
28124             tag: 'li',
28125             cls: 'pull-left header',
28126             html: this.title,
28127             cn : []
28128         };
28129         
28130         if(this.icon){
28131             header.cn.push({
28132                 tag: 'i',
28133                 cls: 'fa ' + this.icon
28134             });
28135         }
28136         
28137         var h = {
28138             tag: 'ul',
28139             cls: 'nav nav-tabs pull-right',
28140             cn: [
28141                 header
28142             ]
28143         };
28144         
28145         if(this.tabScrollable){
28146             h = {
28147                 tag: 'div',
28148                 cls: 'tab-header',
28149                 cn: [
28150                     {
28151                         tag: 'ul',
28152                         cls: 'nav nav-tabs pull-right',
28153                         cn: [
28154                             header
28155                         ]
28156                     }
28157                 ]
28158             };
28159         }
28160         
28161         var cfg = {
28162             tag: 'div',
28163             cls: 'nav-tabs-custom',
28164             cn: [
28165                 h,
28166                 {
28167                     tag: 'div',
28168                     cls: 'tab-content no-padding',
28169                     cn: []
28170                 }
28171             ]
28172         };
28173
28174         return  cfg;
28175     },
28176     initEvents : function()
28177     {
28178         //Roo.log('add add pane handler');
28179         this.on('addpane', this.onAddPane, this);
28180     },
28181      /**
28182      * Updates the box title
28183      * @param {String} html to set the title to.
28184      */
28185     setTitle : function(value)
28186     {
28187         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28188     },
28189     onAddPane : function(pane)
28190     {
28191         this.panes.push(pane);
28192         //Roo.log('addpane');
28193         //Roo.log(pane);
28194         // tabs are rendere left to right..
28195         if(!this.showtabs){
28196             return;
28197         }
28198         
28199         var ctr = this.el.select('.nav-tabs', true).first();
28200          
28201          
28202         var existing = ctr.select('.nav-tab',true);
28203         var qty = existing.getCount();;
28204         
28205         
28206         var tab = ctr.createChild({
28207             tag : 'li',
28208             cls : 'nav-tab' + (qty ? '' : ' active'),
28209             cn : [
28210                 {
28211                     tag : 'a',
28212                     href:'#',
28213                     html : pane.title
28214                 }
28215             ]
28216         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28217         pane.tab = tab;
28218         
28219         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28220         if (!qty) {
28221             pane.el.addClass('active');
28222         }
28223         
28224                 
28225     },
28226     onTabClick : function(ev,un,ob,pane)
28227     {
28228         //Roo.log('tab - prev default');
28229         ev.preventDefault();
28230         
28231         
28232         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28233         pane.tab.addClass('active');
28234         //Roo.log(pane.title);
28235         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28236         // technically we should have a deactivate event.. but maybe add later.
28237         // and it should not de-activate the selected tab...
28238         this.fireEvent('activatepane', pane);
28239         pane.el.addClass('active');
28240         pane.fireEvent('activate');
28241         
28242         
28243     },
28244     
28245     getActivePane : function()
28246     {
28247         var r = false;
28248         Roo.each(this.panes, function(p) {
28249             if(p.el.hasClass('active')){
28250                 r = p;
28251                 return false;
28252             }
28253             
28254             return;
28255         });
28256         
28257         return r;
28258     }
28259     
28260     
28261 });
28262
28263  
28264 /*
28265  * - LGPL
28266  *
28267  * Tab pane
28268  * 
28269  */
28270 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28271 /**
28272  * @class Roo.bootstrap.TabPane
28273  * @extends Roo.bootstrap.Component
28274  * Bootstrap TabPane class
28275  * @cfg {Boolean} active (false | true) Default false
28276  * @cfg {String} title title of panel
28277
28278  * 
28279  * @constructor
28280  * Create a new TabPane
28281  * @param {Object} config The config object
28282  */
28283
28284 Roo.bootstrap.dash.TabPane = function(config){
28285     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28286     
28287     this.addEvents({
28288         // raw events
28289         /**
28290          * @event activate
28291          * When a pane is activated
28292          * @param {Roo.bootstrap.dash.TabPane} pane
28293          */
28294         "activate" : true
28295          
28296     });
28297 };
28298
28299 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28300     
28301     active : false,
28302     title : '',
28303     
28304     // the tabBox that this is attached to.
28305     tab : false,
28306      
28307     getAutoCreate : function() 
28308     {
28309         var cfg = {
28310             tag: 'div',
28311             cls: 'tab-pane'
28312         };
28313         
28314         if(this.active){
28315             cfg.cls += ' active';
28316         }
28317         
28318         return cfg;
28319     },
28320     initEvents  : function()
28321     {
28322         //Roo.log('trigger add pane handler');
28323         this.parent().fireEvent('addpane', this)
28324     },
28325     
28326      /**
28327      * Updates the tab title 
28328      * @param {String} html to set the title to.
28329      */
28330     setTitle: function(str)
28331     {
28332         if (!this.tab) {
28333             return;
28334         }
28335         this.title = str;
28336         this.tab.select('a', true).first().dom.innerHTML = str;
28337         
28338     }
28339     
28340     
28341     
28342 });
28343
28344  
28345
28346
28347  /*
28348  * - LGPL
28349  *
28350  * menu
28351  * 
28352  */
28353 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28354
28355 /**
28356  * @class Roo.bootstrap.menu.Menu
28357  * @extends Roo.bootstrap.Component
28358  * Bootstrap Menu class - container for Menu
28359  * @cfg {String} html Text of the menu
28360  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28361  * @cfg {String} icon Font awesome icon
28362  * @cfg {String} pos Menu align to (top | bottom) default bottom
28363  * 
28364  * 
28365  * @constructor
28366  * Create a new Menu
28367  * @param {Object} config The config object
28368  */
28369
28370
28371 Roo.bootstrap.menu.Menu = function(config){
28372     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28373     
28374     this.addEvents({
28375         /**
28376          * @event beforeshow
28377          * Fires before this menu is displayed
28378          * @param {Roo.bootstrap.menu.Menu} this
28379          */
28380         beforeshow : true,
28381         /**
28382          * @event beforehide
28383          * Fires before this menu is hidden
28384          * @param {Roo.bootstrap.menu.Menu} this
28385          */
28386         beforehide : true,
28387         /**
28388          * @event show
28389          * Fires after this menu is displayed
28390          * @param {Roo.bootstrap.menu.Menu} this
28391          */
28392         show : true,
28393         /**
28394          * @event hide
28395          * Fires after this menu is hidden
28396          * @param {Roo.bootstrap.menu.Menu} this
28397          */
28398         hide : true,
28399         /**
28400          * @event click
28401          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28402          * @param {Roo.bootstrap.menu.Menu} this
28403          * @param {Roo.EventObject} e
28404          */
28405         click : true
28406     });
28407     
28408 };
28409
28410 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28411     
28412     submenu : false,
28413     html : '',
28414     weight : 'default',
28415     icon : false,
28416     pos : 'bottom',
28417     
28418     
28419     getChildContainer : function() {
28420         if(this.isSubMenu){
28421             return this.el;
28422         }
28423         
28424         return this.el.select('ul.dropdown-menu', true).first();  
28425     },
28426     
28427     getAutoCreate : function()
28428     {
28429         var text = [
28430             {
28431                 tag : 'span',
28432                 cls : 'roo-menu-text',
28433                 html : this.html
28434             }
28435         ];
28436         
28437         if(this.icon){
28438             text.unshift({
28439                 tag : 'i',
28440                 cls : 'fa ' + this.icon
28441             })
28442         }
28443         
28444         
28445         var cfg = {
28446             tag : 'div',
28447             cls : 'btn-group',
28448             cn : [
28449                 {
28450                     tag : 'button',
28451                     cls : 'dropdown-button btn btn-' + this.weight,
28452                     cn : text
28453                 },
28454                 {
28455                     tag : 'button',
28456                     cls : 'dropdown-toggle btn btn-' + this.weight,
28457                     cn : [
28458                         {
28459                             tag : 'span',
28460                             cls : 'caret'
28461                         }
28462                     ]
28463                 },
28464                 {
28465                     tag : 'ul',
28466                     cls : 'dropdown-menu'
28467                 }
28468             ]
28469             
28470         };
28471         
28472         if(this.pos == 'top'){
28473             cfg.cls += ' dropup';
28474         }
28475         
28476         if(this.isSubMenu){
28477             cfg = {
28478                 tag : 'ul',
28479                 cls : 'dropdown-menu'
28480             }
28481         }
28482         
28483         return cfg;
28484     },
28485     
28486     onRender : function(ct, position)
28487     {
28488         this.isSubMenu = ct.hasClass('dropdown-submenu');
28489         
28490         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28491     },
28492     
28493     initEvents : function() 
28494     {
28495         if(this.isSubMenu){
28496             return;
28497         }
28498         
28499         this.hidden = true;
28500         
28501         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28502         this.triggerEl.on('click', this.onTriggerPress, this);
28503         
28504         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28505         this.buttonEl.on('click', this.onClick, this);
28506         
28507     },
28508     
28509     list : function()
28510     {
28511         if(this.isSubMenu){
28512             return this.el;
28513         }
28514         
28515         return this.el.select('ul.dropdown-menu', true).first();
28516     },
28517     
28518     onClick : function(e)
28519     {
28520         this.fireEvent("click", this, e);
28521     },
28522     
28523     onTriggerPress  : function(e)
28524     {   
28525         if (this.isVisible()) {
28526             this.hide();
28527         } else {
28528             this.show();
28529         }
28530     },
28531     
28532     isVisible : function(){
28533         return !this.hidden;
28534     },
28535     
28536     show : function()
28537     {
28538         this.fireEvent("beforeshow", this);
28539         
28540         this.hidden = false;
28541         this.el.addClass('open');
28542         
28543         Roo.get(document).on("mouseup", this.onMouseUp, this);
28544         
28545         this.fireEvent("show", this);
28546         
28547         
28548     },
28549     
28550     hide : function()
28551     {
28552         this.fireEvent("beforehide", this);
28553         
28554         this.hidden = true;
28555         this.el.removeClass('open');
28556         
28557         Roo.get(document).un("mouseup", this.onMouseUp);
28558         
28559         this.fireEvent("hide", this);
28560     },
28561     
28562     onMouseUp : function()
28563     {
28564         this.hide();
28565     }
28566     
28567 });
28568
28569  
28570  /*
28571  * - LGPL
28572  *
28573  * menu item
28574  * 
28575  */
28576 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28577
28578 /**
28579  * @class Roo.bootstrap.menu.Item
28580  * @extends Roo.bootstrap.Component
28581  * Bootstrap MenuItem class
28582  * @cfg {Boolean} submenu (true | false) default false
28583  * @cfg {String} html text of the item
28584  * @cfg {String} href the link
28585  * @cfg {Boolean} disable (true | false) default false
28586  * @cfg {Boolean} preventDefault (true | false) default true
28587  * @cfg {String} icon Font awesome icon
28588  * @cfg {String} pos Submenu align to (left | right) default right 
28589  * 
28590  * 
28591  * @constructor
28592  * Create a new Item
28593  * @param {Object} config The config object
28594  */
28595
28596
28597 Roo.bootstrap.menu.Item = function(config){
28598     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28599     this.addEvents({
28600         /**
28601          * @event mouseover
28602          * Fires when the mouse is hovering over this menu
28603          * @param {Roo.bootstrap.menu.Item} this
28604          * @param {Roo.EventObject} e
28605          */
28606         mouseover : true,
28607         /**
28608          * @event mouseout
28609          * Fires when the mouse exits this menu
28610          * @param {Roo.bootstrap.menu.Item} this
28611          * @param {Roo.EventObject} e
28612          */
28613         mouseout : true,
28614         // raw events
28615         /**
28616          * @event click
28617          * The raw click event for the entire grid.
28618          * @param {Roo.EventObject} e
28619          */
28620         click : true
28621     });
28622 };
28623
28624 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28625     
28626     submenu : false,
28627     href : '',
28628     html : '',
28629     preventDefault: true,
28630     disable : false,
28631     icon : false,
28632     pos : 'right',
28633     
28634     getAutoCreate : function()
28635     {
28636         var text = [
28637             {
28638                 tag : 'span',
28639                 cls : 'roo-menu-item-text',
28640                 html : this.html
28641             }
28642         ];
28643         
28644         if(this.icon){
28645             text.unshift({
28646                 tag : 'i',
28647                 cls : 'fa ' + this.icon
28648             })
28649         }
28650         
28651         var cfg = {
28652             tag : 'li',
28653             cn : [
28654                 {
28655                     tag : 'a',
28656                     href : this.href || '#',
28657                     cn : text
28658                 }
28659             ]
28660         };
28661         
28662         if(this.disable){
28663             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28664         }
28665         
28666         if(this.submenu){
28667             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28668             
28669             if(this.pos == 'left'){
28670                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28671             }
28672         }
28673         
28674         return cfg;
28675     },
28676     
28677     initEvents : function() 
28678     {
28679         this.el.on('mouseover', this.onMouseOver, this);
28680         this.el.on('mouseout', this.onMouseOut, this);
28681         
28682         this.el.select('a', true).first().on('click', this.onClick, this);
28683         
28684     },
28685     
28686     onClick : function(e)
28687     {
28688         if(this.preventDefault){
28689             e.preventDefault();
28690         }
28691         
28692         this.fireEvent("click", this, e);
28693     },
28694     
28695     onMouseOver : function(e)
28696     {
28697         if(this.submenu && this.pos == 'left'){
28698             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28699         }
28700         
28701         this.fireEvent("mouseover", this, e);
28702     },
28703     
28704     onMouseOut : function(e)
28705     {
28706         this.fireEvent("mouseout", this, e);
28707     }
28708 });
28709
28710  
28711
28712  /*
28713  * - LGPL
28714  *
28715  * menu separator
28716  * 
28717  */
28718 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28719
28720 /**
28721  * @class Roo.bootstrap.menu.Separator
28722  * @extends Roo.bootstrap.Component
28723  * Bootstrap Separator class
28724  * 
28725  * @constructor
28726  * Create a new Separator
28727  * @param {Object} config The config object
28728  */
28729
28730
28731 Roo.bootstrap.menu.Separator = function(config){
28732     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28733 };
28734
28735 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28736     
28737     getAutoCreate : function(){
28738         var cfg = {
28739             tag : 'li',
28740             cls: 'divider'
28741         };
28742         
28743         return cfg;
28744     }
28745    
28746 });
28747
28748  
28749
28750  /*
28751  * - LGPL
28752  *
28753  * Tooltip
28754  * 
28755  */
28756
28757 /**
28758  * @class Roo.bootstrap.Tooltip
28759  * Bootstrap Tooltip class
28760  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28761  * to determine which dom element triggers the tooltip.
28762  * 
28763  * It needs to add support for additional attributes like tooltip-position
28764  * 
28765  * @constructor
28766  * Create a new Toolti
28767  * @param {Object} config The config object
28768  */
28769
28770 Roo.bootstrap.Tooltip = function(config){
28771     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28772     
28773     this.alignment = Roo.bootstrap.Tooltip.alignment;
28774     
28775     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28776         this.alignment = config.alignment;
28777     }
28778     
28779 };
28780
28781 Roo.apply(Roo.bootstrap.Tooltip, {
28782     /**
28783      * @function init initialize tooltip monitoring.
28784      * @static
28785      */
28786     currentEl : false,
28787     currentTip : false,
28788     currentRegion : false,
28789     
28790     //  init : delay?
28791     
28792     init : function()
28793     {
28794         Roo.get(document).on('mouseover', this.enter ,this);
28795         Roo.get(document).on('mouseout', this.leave, this);
28796          
28797         
28798         this.currentTip = new Roo.bootstrap.Tooltip();
28799     },
28800     
28801     enter : function(ev)
28802     {
28803         var dom = ev.getTarget();
28804         
28805         //Roo.log(['enter',dom]);
28806         var el = Roo.fly(dom);
28807         if (this.currentEl) {
28808             //Roo.log(dom);
28809             //Roo.log(this.currentEl);
28810             //Roo.log(this.currentEl.contains(dom));
28811             if (this.currentEl == el) {
28812                 return;
28813             }
28814             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28815                 return;
28816             }
28817
28818         }
28819         
28820         if (this.currentTip.el) {
28821             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28822         }    
28823         //Roo.log(ev);
28824         
28825         if(!el || el.dom == document){
28826             return;
28827         }
28828         
28829         var bindEl = el;
28830         
28831         // you can not look for children, as if el is the body.. then everythign is the child..
28832         if (!el.attr('tooltip')) { //
28833             if (!el.select("[tooltip]").elements.length) {
28834                 return;
28835             }
28836             // is the mouse over this child...?
28837             bindEl = el.select("[tooltip]").first();
28838             var xy = ev.getXY();
28839             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28840                 //Roo.log("not in region.");
28841                 return;
28842             }
28843             //Roo.log("child element over..");
28844             
28845         }
28846         this.currentEl = bindEl;
28847         this.currentTip.bind(bindEl);
28848         this.currentRegion = Roo.lib.Region.getRegion(dom);
28849         this.currentTip.enter();
28850         
28851     },
28852     leave : function(ev)
28853     {
28854         var dom = ev.getTarget();
28855         //Roo.log(['leave',dom]);
28856         if (!this.currentEl) {
28857             return;
28858         }
28859         
28860         
28861         if (dom != this.currentEl.dom) {
28862             return;
28863         }
28864         var xy = ev.getXY();
28865         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28866             return;
28867         }
28868         // only activate leave if mouse cursor is outside... bounding box..
28869         
28870         
28871         
28872         
28873         if (this.currentTip) {
28874             this.currentTip.leave();
28875         }
28876         //Roo.log('clear currentEl');
28877         this.currentEl = false;
28878         
28879         
28880     },
28881     alignment : {
28882         'left' : ['r-l', [-2,0], 'right'],
28883         'right' : ['l-r', [2,0], 'left'],
28884         'bottom' : ['t-b', [0,2], 'top'],
28885         'top' : [ 'b-t', [0,-2], 'bottom']
28886     }
28887     
28888 });
28889
28890
28891 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28892     
28893     
28894     bindEl : false,
28895     
28896     delay : null, // can be { show : 300 , hide: 500}
28897     
28898     timeout : null,
28899     
28900     hoverState : null, //???
28901     
28902     placement : 'bottom', 
28903     
28904     alignment : false,
28905     
28906     getAutoCreate : function(){
28907     
28908         var cfg = {
28909            cls : 'tooltip',   
28910            role : 'tooltip',
28911            cn : [
28912                 {
28913                     cls : 'tooltip-arrow arrow'
28914                 },
28915                 {
28916                     cls : 'tooltip-inner'
28917                 }
28918            ]
28919         };
28920         
28921         return cfg;
28922     },
28923     bind : function(el)
28924     {
28925         this.bindEl = el;
28926     },
28927     
28928     initEvents : function()
28929     {
28930         this.arrowEl = this.el.select('.arrow', true).first();
28931         this.innerEl = this.el.select('.tooltip-inner', true).first();
28932     },
28933     
28934     enter : function () {
28935        
28936         if (this.timeout != null) {
28937             clearTimeout(this.timeout);
28938         }
28939         
28940         this.hoverState = 'in';
28941          //Roo.log("enter - show");
28942         if (!this.delay || !this.delay.show) {
28943             this.show();
28944             return;
28945         }
28946         var _t = this;
28947         this.timeout = setTimeout(function () {
28948             if (_t.hoverState == 'in') {
28949                 _t.show();
28950             }
28951         }, this.delay.show);
28952     },
28953     leave : function()
28954     {
28955         clearTimeout(this.timeout);
28956     
28957         this.hoverState = 'out';
28958          if (!this.delay || !this.delay.hide) {
28959             this.hide();
28960             return;
28961         }
28962        
28963         var _t = this;
28964         this.timeout = setTimeout(function () {
28965             //Roo.log("leave - timeout");
28966             
28967             if (_t.hoverState == 'out') {
28968                 _t.hide();
28969                 Roo.bootstrap.Tooltip.currentEl = false;
28970             }
28971         }, delay);
28972     },
28973     
28974     show : function (msg)
28975     {
28976         if (!this.el) {
28977             this.render(document.body);
28978         }
28979         // set content.
28980         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28981         
28982         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28983         
28984         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28985         
28986         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28987                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28988         
28989         var placement = typeof this.placement == 'function' ?
28990             this.placement.call(this, this.el, on_el) :
28991             this.placement;
28992             
28993         var autoToken = /\s?auto?\s?/i;
28994         var autoPlace = autoToken.test(placement);
28995         if (autoPlace) {
28996             placement = placement.replace(autoToken, '') || 'top';
28997         }
28998         
28999         //this.el.detach()
29000         //this.el.setXY([0,0]);
29001         this.el.show();
29002         //this.el.dom.style.display='block';
29003         
29004         //this.el.appendTo(on_el);
29005         
29006         var p = this.getPosition();
29007         var box = this.el.getBox();
29008         
29009         if (autoPlace) {
29010             // fixme..
29011         }
29012         
29013         var align = this.alignment[placement];
29014         
29015         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29016         
29017         if(placement == 'top' || placement == 'bottom'){
29018             if(xy[0] < 0){
29019                 placement = 'right';
29020             }
29021             
29022             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29023                 placement = 'left';
29024             }
29025             
29026             var scroll = Roo.select('body', true).first().getScroll();
29027             
29028             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29029                 placement = 'top';
29030             }
29031             
29032             align = this.alignment[placement];
29033             
29034             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29035             
29036         }
29037         
29038         this.el.alignTo(this.bindEl, align[0],align[1]);
29039         //var arrow = this.el.select('.arrow',true).first();
29040         //arrow.set(align[2], 
29041         
29042         this.el.addClass(placement);
29043         this.el.addClass("bs-tooltip-"+ placement);
29044         
29045         this.el.addClass('in fade show');
29046         
29047         this.hoverState = null;
29048         
29049         if (this.el.hasClass('fade')) {
29050             // fade it?
29051         }
29052         
29053         
29054         
29055         
29056         
29057     },
29058     hide : function()
29059     {
29060          
29061         if (!this.el) {
29062             return;
29063         }
29064         //this.el.setXY([0,0]);
29065         this.el.removeClass(['show', 'in']);
29066         //this.el.hide();
29067         
29068     }
29069     
29070 });
29071  
29072
29073  /*
29074  * - LGPL
29075  *
29076  * Location Picker
29077  * 
29078  */
29079
29080 /**
29081  * @class Roo.bootstrap.LocationPicker
29082  * @extends Roo.bootstrap.Component
29083  * Bootstrap LocationPicker class
29084  * @cfg {Number} latitude Position when init default 0
29085  * @cfg {Number} longitude Position when init default 0
29086  * @cfg {Number} zoom default 15
29087  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29088  * @cfg {Boolean} mapTypeControl default false
29089  * @cfg {Boolean} disableDoubleClickZoom default false
29090  * @cfg {Boolean} scrollwheel default true
29091  * @cfg {Boolean} streetViewControl default false
29092  * @cfg {Number} radius default 0
29093  * @cfg {String} locationName
29094  * @cfg {Boolean} draggable default true
29095  * @cfg {Boolean} enableAutocomplete default false
29096  * @cfg {Boolean} enableReverseGeocode default true
29097  * @cfg {String} markerTitle
29098  * 
29099  * @constructor
29100  * Create a new LocationPicker
29101  * @param {Object} config The config object
29102  */
29103
29104
29105 Roo.bootstrap.LocationPicker = function(config){
29106     
29107     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29108     
29109     this.addEvents({
29110         /**
29111          * @event initial
29112          * Fires when the picker initialized.
29113          * @param {Roo.bootstrap.LocationPicker} this
29114          * @param {Google Location} location
29115          */
29116         initial : true,
29117         /**
29118          * @event positionchanged
29119          * Fires when the picker position changed.
29120          * @param {Roo.bootstrap.LocationPicker} this
29121          * @param {Google Location} location
29122          */
29123         positionchanged : true,
29124         /**
29125          * @event resize
29126          * Fires when the map resize.
29127          * @param {Roo.bootstrap.LocationPicker} this
29128          */
29129         resize : true,
29130         /**
29131          * @event show
29132          * Fires when the map show.
29133          * @param {Roo.bootstrap.LocationPicker} this
29134          */
29135         show : true,
29136         /**
29137          * @event hide
29138          * Fires when the map hide.
29139          * @param {Roo.bootstrap.LocationPicker} this
29140          */
29141         hide : true,
29142         /**
29143          * @event mapClick
29144          * Fires when click the map.
29145          * @param {Roo.bootstrap.LocationPicker} this
29146          * @param {Map event} e
29147          */
29148         mapClick : true,
29149         /**
29150          * @event mapRightClick
29151          * Fires when right click the map.
29152          * @param {Roo.bootstrap.LocationPicker} this
29153          * @param {Map event} e
29154          */
29155         mapRightClick : true,
29156         /**
29157          * @event markerClick
29158          * Fires when click the marker.
29159          * @param {Roo.bootstrap.LocationPicker} this
29160          * @param {Map event} e
29161          */
29162         markerClick : true,
29163         /**
29164          * @event markerRightClick
29165          * Fires when right click the marker.
29166          * @param {Roo.bootstrap.LocationPicker} this
29167          * @param {Map event} e
29168          */
29169         markerRightClick : true,
29170         /**
29171          * @event OverlayViewDraw
29172          * Fires when OverlayView Draw
29173          * @param {Roo.bootstrap.LocationPicker} this
29174          */
29175         OverlayViewDraw : true,
29176         /**
29177          * @event OverlayViewOnAdd
29178          * Fires when OverlayView Draw
29179          * @param {Roo.bootstrap.LocationPicker} this
29180          */
29181         OverlayViewOnAdd : true,
29182         /**
29183          * @event OverlayViewOnRemove
29184          * Fires when OverlayView Draw
29185          * @param {Roo.bootstrap.LocationPicker} this
29186          */
29187         OverlayViewOnRemove : true,
29188         /**
29189          * @event OverlayViewShow
29190          * Fires when OverlayView Draw
29191          * @param {Roo.bootstrap.LocationPicker} this
29192          * @param {Pixel} cpx
29193          */
29194         OverlayViewShow : true,
29195         /**
29196          * @event OverlayViewHide
29197          * Fires when OverlayView Draw
29198          * @param {Roo.bootstrap.LocationPicker} this
29199          */
29200         OverlayViewHide : true,
29201         /**
29202          * @event loadexception
29203          * Fires when load google lib failed.
29204          * @param {Roo.bootstrap.LocationPicker} this
29205          */
29206         loadexception : true
29207     });
29208         
29209 };
29210
29211 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29212     
29213     gMapContext: false,
29214     
29215     latitude: 0,
29216     longitude: 0,
29217     zoom: 15,
29218     mapTypeId: false,
29219     mapTypeControl: false,
29220     disableDoubleClickZoom: false,
29221     scrollwheel: true,
29222     streetViewControl: false,
29223     radius: 0,
29224     locationName: '',
29225     draggable: true,
29226     enableAutocomplete: false,
29227     enableReverseGeocode: true,
29228     markerTitle: '',
29229     
29230     getAutoCreate: function()
29231     {
29232
29233         var cfg = {
29234             tag: 'div',
29235             cls: 'roo-location-picker'
29236         };
29237         
29238         return cfg
29239     },
29240     
29241     initEvents: function(ct, position)
29242     {       
29243         if(!this.el.getWidth() || this.isApplied()){
29244             return;
29245         }
29246         
29247         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29248         
29249         this.initial();
29250     },
29251     
29252     initial: function()
29253     {
29254         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29255             this.fireEvent('loadexception', this);
29256             return;
29257         }
29258         
29259         if(!this.mapTypeId){
29260             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29261         }
29262         
29263         this.gMapContext = this.GMapContext();
29264         
29265         this.initOverlayView();
29266         
29267         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29268         
29269         var _this = this;
29270                 
29271         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29272             _this.setPosition(_this.gMapContext.marker.position);
29273         });
29274         
29275         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29276             _this.fireEvent('mapClick', this, event);
29277             
29278         });
29279
29280         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29281             _this.fireEvent('mapRightClick', this, event);
29282             
29283         });
29284         
29285         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29286             _this.fireEvent('markerClick', this, event);
29287             
29288         });
29289
29290         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29291             _this.fireEvent('markerRightClick', this, event);
29292             
29293         });
29294         
29295         this.setPosition(this.gMapContext.location);
29296         
29297         this.fireEvent('initial', this, this.gMapContext.location);
29298     },
29299     
29300     initOverlayView: function()
29301     {
29302         var _this = this;
29303         
29304         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29305             
29306             draw: function()
29307             {
29308                 _this.fireEvent('OverlayViewDraw', _this);
29309             },
29310             
29311             onAdd: function()
29312             {
29313                 _this.fireEvent('OverlayViewOnAdd', _this);
29314             },
29315             
29316             onRemove: function()
29317             {
29318                 _this.fireEvent('OverlayViewOnRemove', _this);
29319             },
29320             
29321             show: function(cpx)
29322             {
29323                 _this.fireEvent('OverlayViewShow', _this, cpx);
29324             },
29325             
29326             hide: function()
29327             {
29328                 _this.fireEvent('OverlayViewHide', _this);
29329             }
29330             
29331         });
29332     },
29333     
29334     fromLatLngToContainerPixel: function(event)
29335     {
29336         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29337     },
29338     
29339     isApplied: function() 
29340     {
29341         return this.getGmapContext() == false ? false : true;
29342     },
29343     
29344     getGmapContext: function() 
29345     {
29346         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29347     },
29348     
29349     GMapContext: function() 
29350     {
29351         var position = new google.maps.LatLng(this.latitude, this.longitude);
29352         
29353         var _map = new google.maps.Map(this.el.dom, {
29354             center: position,
29355             zoom: this.zoom,
29356             mapTypeId: this.mapTypeId,
29357             mapTypeControl: this.mapTypeControl,
29358             disableDoubleClickZoom: this.disableDoubleClickZoom,
29359             scrollwheel: this.scrollwheel,
29360             streetViewControl: this.streetViewControl,
29361             locationName: this.locationName,
29362             draggable: this.draggable,
29363             enableAutocomplete: this.enableAutocomplete,
29364             enableReverseGeocode: this.enableReverseGeocode
29365         });
29366         
29367         var _marker = new google.maps.Marker({
29368             position: position,
29369             map: _map,
29370             title: this.markerTitle,
29371             draggable: this.draggable
29372         });
29373         
29374         return {
29375             map: _map,
29376             marker: _marker,
29377             circle: null,
29378             location: position,
29379             radius: this.radius,
29380             locationName: this.locationName,
29381             addressComponents: {
29382                 formatted_address: null,
29383                 addressLine1: null,
29384                 addressLine2: null,
29385                 streetName: null,
29386                 streetNumber: null,
29387                 city: null,
29388                 district: null,
29389                 state: null,
29390                 stateOrProvince: null
29391             },
29392             settings: this,
29393             domContainer: this.el.dom,
29394             geodecoder: new google.maps.Geocoder()
29395         };
29396     },
29397     
29398     drawCircle: function(center, radius, options) 
29399     {
29400         if (this.gMapContext.circle != null) {
29401             this.gMapContext.circle.setMap(null);
29402         }
29403         if (radius > 0) {
29404             radius *= 1;
29405             options = Roo.apply({}, options, {
29406                 strokeColor: "#0000FF",
29407                 strokeOpacity: .35,
29408                 strokeWeight: 2,
29409                 fillColor: "#0000FF",
29410                 fillOpacity: .2
29411             });
29412             
29413             options.map = this.gMapContext.map;
29414             options.radius = radius;
29415             options.center = center;
29416             this.gMapContext.circle = new google.maps.Circle(options);
29417             return this.gMapContext.circle;
29418         }
29419         
29420         return null;
29421     },
29422     
29423     setPosition: function(location) 
29424     {
29425         this.gMapContext.location = location;
29426         this.gMapContext.marker.setPosition(location);
29427         this.gMapContext.map.panTo(location);
29428         this.drawCircle(location, this.gMapContext.radius, {});
29429         
29430         var _this = this;
29431         
29432         if (this.gMapContext.settings.enableReverseGeocode) {
29433             this.gMapContext.geodecoder.geocode({
29434                 latLng: this.gMapContext.location
29435             }, function(results, status) {
29436                 
29437                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29438                     _this.gMapContext.locationName = results[0].formatted_address;
29439                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29440                     
29441                     _this.fireEvent('positionchanged', this, location);
29442                 }
29443             });
29444             
29445             return;
29446         }
29447         
29448         this.fireEvent('positionchanged', this, location);
29449     },
29450     
29451     resize: function()
29452     {
29453         google.maps.event.trigger(this.gMapContext.map, "resize");
29454         
29455         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29456         
29457         this.fireEvent('resize', this);
29458     },
29459     
29460     setPositionByLatLng: function(latitude, longitude)
29461     {
29462         this.setPosition(new google.maps.LatLng(latitude, longitude));
29463     },
29464     
29465     getCurrentPosition: function() 
29466     {
29467         return {
29468             latitude: this.gMapContext.location.lat(),
29469             longitude: this.gMapContext.location.lng()
29470         };
29471     },
29472     
29473     getAddressName: function() 
29474     {
29475         return this.gMapContext.locationName;
29476     },
29477     
29478     getAddressComponents: function() 
29479     {
29480         return this.gMapContext.addressComponents;
29481     },
29482     
29483     address_component_from_google_geocode: function(address_components) 
29484     {
29485         var result = {};
29486         
29487         for (var i = 0; i < address_components.length; i++) {
29488             var component = address_components[i];
29489             if (component.types.indexOf("postal_code") >= 0) {
29490                 result.postalCode = component.short_name;
29491             } else if (component.types.indexOf("street_number") >= 0) {
29492                 result.streetNumber = component.short_name;
29493             } else if (component.types.indexOf("route") >= 0) {
29494                 result.streetName = component.short_name;
29495             } else if (component.types.indexOf("neighborhood") >= 0) {
29496                 result.city = component.short_name;
29497             } else if (component.types.indexOf("locality") >= 0) {
29498                 result.city = component.short_name;
29499             } else if (component.types.indexOf("sublocality") >= 0) {
29500                 result.district = component.short_name;
29501             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29502                 result.stateOrProvince = component.short_name;
29503             } else if (component.types.indexOf("country") >= 0) {
29504                 result.country = component.short_name;
29505             }
29506         }
29507         
29508         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29509         result.addressLine2 = "";
29510         return result;
29511     },
29512     
29513     setZoomLevel: function(zoom)
29514     {
29515         this.gMapContext.map.setZoom(zoom);
29516     },
29517     
29518     show: function()
29519     {
29520         if(!this.el){
29521             return;
29522         }
29523         
29524         this.el.show();
29525         
29526         this.resize();
29527         
29528         this.fireEvent('show', this);
29529     },
29530     
29531     hide: function()
29532     {
29533         if(!this.el){
29534             return;
29535         }
29536         
29537         this.el.hide();
29538         
29539         this.fireEvent('hide', this);
29540     }
29541     
29542 });
29543
29544 Roo.apply(Roo.bootstrap.LocationPicker, {
29545     
29546     OverlayView : function(map, options)
29547     {
29548         options = options || {};
29549         
29550         this.setMap(map);
29551     }
29552     
29553     
29554 });/**
29555  * @class Roo.bootstrap.Alert
29556  * @extends Roo.bootstrap.Component
29557  * Bootstrap Alert class - shows an alert area box
29558  * eg
29559  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29560   Enter a valid email address
29561 </div>
29562  * @licence LGPL
29563  * @cfg {String} title The title of alert
29564  * @cfg {String} html The content of alert
29565  * @cfg {String} weight (  success | info | warning | danger )
29566  * @cfg {String} faicon font-awesomeicon
29567  * 
29568  * @constructor
29569  * Create a new alert
29570  * @param {Object} config The config object
29571  */
29572
29573
29574 Roo.bootstrap.Alert = function(config){
29575     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29576     
29577 };
29578
29579 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29580     
29581     title: '',
29582     html: '',
29583     weight: false,
29584     faicon: false,
29585     
29586     getAutoCreate : function()
29587     {
29588         
29589         var cfg = {
29590             tag : 'div',
29591             cls : 'alert',
29592             cn : [
29593                 {
29594                     tag : 'i',
29595                     cls : 'roo-alert-icon'
29596                     
29597                 },
29598                 {
29599                     tag : 'b',
29600                     cls : 'roo-alert-title',
29601                     html : this.title
29602                 },
29603                 {
29604                     tag : 'span',
29605                     cls : 'roo-alert-text',
29606                     html : this.html
29607                 }
29608             ]
29609         };
29610         
29611         if(this.faicon){
29612             cfg.cn[0].cls += ' fa ' + this.faicon;
29613         }
29614         
29615         if(this.weight){
29616             cfg.cls += ' alert-' + this.weight;
29617         }
29618         
29619         return cfg;
29620     },
29621     
29622     initEvents: function() 
29623     {
29624         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29625     },
29626     
29627     setTitle : function(str)
29628     {
29629         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29630     },
29631     
29632     setText : function(str)
29633     {
29634         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29635     },
29636     
29637     setWeight : function(weight)
29638     {
29639         if(this.weight){
29640             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29641         }
29642         
29643         this.weight = weight;
29644         
29645         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29646     },
29647     
29648     setIcon : function(icon)
29649     {
29650         if(this.faicon){
29651             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29652         }
29653         
29654         this.faicon = icon;
29655         
29656         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29657     },
29658     
29659     hide: function() 
29660     {
29661         this.el.hide();   
29662     },
29663     
29664     show: function() 
29665     {  
29666         this.el.show();   
29667     }
29668     
29669 });
29670
29671  
29672 /*
29673 * Licence: LGPL
29674 */
29675
29676 /**
29677  * @class Roo.bootstrap.UploadCropbox
29678  * @extends Roo.bootstrap.Component
29679  * Bootstrap UploadCropbox class
29680  * @cfg {String} emptyText show when image has been loaded
29681  * @cfg {String} rotateNotify show when image too small to rotate
29682  * @cfg {Number} errorTimeout default 3000
29683  * @cfg {Number} minWidth default 300
29684  * @cfg {Number} minHeight default 300
29685  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29686  * @cfg {Boolean} isDocument (true|false) default false
29687  * @cfg {String} url action url
29688  * @cfg {String} paramName default 'imageUpload'
29689  * @cfg {String} method default POST
29690  * @cfg {Boolean} loadMask (true|false) default true
29691  * @cfg {Boolean} loadingText default 'Loading...'
29692  * 
29693  * @constructor
29694  * Create a new UploadCropbox
29695  * @param {Object} config The config object
29696  */
29697
29698 Roo.bootstrap.UploadCropbox = function(config){
29699     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29700     
29701     this.addEvents({
29702         /**
29703          * @event beforeselectfile
29704          * Fire before select file
29705          * @param {Roo.bootstrap.UploadCropbox} this
29706          */
29707         "beforeselectfile" : true,
29708         /**
29709          * @event initial
29710          * Fire after initEvent
29711          * @param {Roo.bootstrap.UploadCropbox} this
29712          */
29713         "initial" : true,
29714         /**
29715          * @event crop
29716          * Fire after initEvent
29717          * @param {Roo.bootstrap.UploadCropbox} this
29718          * @param {String} data
29719          */
29720         "crop" : true,
29721         /**
29722          * @event prepare
29723          * Fire when preparing the file data
29724          * @param {Roo.bootstrap.UploadCropbox} this
29725          * @param {Object} file
29726          */
29727         "prepare" : true,
29728         /**
29729          * @event exception
29730          * Fire when get exception
29731          * @param {Roo.bootstrap.UploadCropbox} this
29732          * @param {XMLHttpRequest} xhr
29733          */
29734         "exception" : true,
29735         /**
29736          * @event beforeloadcanvas
29737          * Fire before load the canvas
29738          * @param {Roo.bootstrap.UploadCropbox} this
29739          * @param {String} src
29740          */
29741         "beforeloadcanvas" : true,
29742         /**
29743          * @event trash
29744          * Fire when trash image
29745          * @param {Roo.bootstrap.UploadCropbox} this
29746          */
29747         "trash" : true,
29748         /**
29749          * @event download
29750          * Fire when download the image
29751          * @param {Roo.bootstrap.UploadCropbox} this
29752          */
29753         "download" : true,
29754         /**
29755          * @event footerbuttonclick
29756          * Fire when footerbuttonclick
29757          * @param {Roo.bootstrap.UploadCropbox} this
29758          * @param {String} type
29759          */
29760         "footerbuttonclick" : true,
29761         /**
29762          * @event resize
29763          * Fire when resize
29764          * @param {Roo.bootstrap.UploadCropbox} this
29765          */
29766         "resize" : true,
29767         /**
29768          * @event rotate
29769          * Fire when rotate the image
29770          * @param {Roo.bootstrap.UploadCropbox} this
29771          * @param {String} pos
29772          */
29773         "rotate" : true,
29774         /**
29775          * @event inspect
29776          * Fire when inspect the file
29777          * @param {Roo.bootstrap.UploadCropbox} this
29778          * @param {Object} file
29779          */
29780         "inspect" : true,
29781         /**
29782          * @event upload
29783          * Fire when xhr upload the file
29784          * @param {Roo.bootstrap.UploadCropbox} this
29785          * @param {Object} data
29786          */
29787         "upload" : true,
29788         /**
29789          * @event arrange
29790          * Fire when arrange the file data
29791          * @param {Roo.bootstrap.UploadCropbox} this
29792          * @param {Object} formData
29793          */
29794         "arrange" : true
29795     });
29796     
29797     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29798 };
29799
29800 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29801     
29802     emptyText : 'Click to upload image',
29803     rotateNotify : 'Image is too small to rotate',
29804     errorTimeout : 3000,
29805     scale : 0,
29806     baseScale : 1,
29807     rotate : 0,
29808     dragable : false,
29809     pinching : false,
29810     mouseX : 0,
29811     mouseY : 0,
29812     cropData : false,
29813     minWidth : 300,
29814     minHeight : 300,
29815     file : false,
29816     exif : {},
29817     baseRotate : 1,
29818     cropType : 'image/jpeg',
29819     buttons : false,
29820     canvasLoaded : false,
29821     isDocument : false,
29822     method : 'POST',
29823     paramName : 'imageUpload',
29824     loadMask : true,
29825     loadingText : 'Loading...',
29826     maskEl : false,
29827     
29828     getAutoCreate : function()
29829     {
29830         var cfg = {
29831             tag : 'div',
29832             cls : 'roo-upload-cropbox',
29833             cn : [
29834                 {
29835                     tag : 'input',
29836                     cls : 'roo-upload-cropbox-selector',
29837                     type : 'file'
29838                 },
29839                 {
29840                     tag : 'div',
29841                     cls : 'roo-upload-cropbox-body',
29842                     style : 'cursor:pointer',
29843                     cn : [
29844                         {
29845                             tag : 'div',
29846                             cls : 'roo-upload-cropbox-preview'
29847                         },
29848                         {
29849                             tag : 'div',
29850                             cls : 'roo-upload-cropbox-thumb'
29851                         },
29852                         {
29853                             tag : 'div',
29854                             cls : 'roo-upload-cropbox-empty-notify',
29855                             html : this.emptyText
29856                         },
29857                         {
29858                             tag : 'div',
29859                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29860                             html : this.rotateNotify
29861                         }
29862                     ]
29863                 },
29864                 {
29865                     tag : 'div',
29866                     cls : 'roo-upload-cropbox-footer',
29867                     cn : {
29868                         tag : 'div',
29869                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29870                         cn : []
29871                     }
29872                 }
29873             ]
29874         };
29875         
29876         return cfg;
29877     },
29878     
29879     onRender : function(ct, position)
29880     {
29881         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29882         
29883         if (this.buttons.length) {
29884             
29885             Roo.each(this.buttons, function(bb) {
29886                 
29887                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29888                 
29889                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29890                 
29891             }, this);
29892         }
29893         
29894         if(this.loadMask){
29895             this.maskEl = this.el;
29896         }
29897     },
29898     
29899     initEvents : function()
29900     {
29901         this.urlAPI = (window.createObjectURL && window) || 
29902                                 (window.URL && URL.revokeObjectURL && URL) || 
29903                                 (window.webkitURL && webkitURL);
29904                         
29905         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29906         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29907         
29908         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29909         this.selectorEl.hide();
29910         
29911         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29912         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29913         
29914         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29915         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29916         this.thumbEl.hide();
29917         
29918         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29919         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29920         
29921         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29922         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29923         this.errorEl.hide();
29924         
29925         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29926         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29927         this.footerEl.hide();
29928         
29929         this.setThumbBoxSize();
29930         
29931         this.bind();
29932         
29933         this.resize();
29934         
29935         this.fireEvent('initial', this);
29936     },
29937
29938     bind : function()
29939     {
29940         var _this = this;
29941         
29942         window.addEventListener("resize", function() { _this.resize(); } );
29943         
29944         this.bodyEl.on('click', this.beforeSelectFile, this);
29945         
29946         if(Roo.isTouch){
29947             this.bodyEl.on('touchstart', this.onTouchStart, this);
29948             this.bodyEl.on('touchmove', this.onTouchMove, this);
29949             this.bodyEl.on('touchend', this.onTouchEnd, this);
29950         }
29951         
29952         if(!Roo.isTouch){
29953             this.bodyEl.on('mousedown', this.onMouseDown, this);
29954             this.bodyEl.on('mousemove', this.onMouseMove, this);
29955             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29956             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29957             Roo.get(document).on('mouseup', this.onMouseUp, this);
29958         }
29959         
29960         this.selectorEl.on('change', this.onFileSelected, this);
29961     },
29962     
29963     reset : function()
29964     {    
29965         this.scale = 0;
29966         this.baseScale = 1;
29967         this.rotate = 0;
29968         this.baseRotate = 1;
29969         this.dragable = false;
29970         this.pinching = false;
29971         this.mouseX = 0;
29972         this.mouseY = 0;
29973         this.cropData = false;
29974         this.notifyEl.dom.innerHTML = this.emptyText;
29975         
29976         this.selectorEl.dom.value = '';
29977         
29978     },
29979     
29980     resize : function()
29981     {
29982         if(this.fireEvent('resize', this) != false){
29983             this.setThumbBoxPosition();
29984             this.setCanvasPosition();
29985         }
29986     },
29987     
29988     onFooterButtonClick : function(e, el, o, type)
29989     {
29990         switch (type) {
29991             case 'rotate-left' :
29992                 this.onRotateLeft(e);
29993                 break;
29994             case 'rotate-right' :
29995                 this.onRotateRight(e);
29996                 break;
29997             case 'picture' :
29998                 this.beforeSelectFile(e);
29999                 break;
30000             case 'trash' :
30001                 this.trash(e);
30002                 break;
30003             case 'crop' :
30004                 this.crop(e);
30005                 break;
30006             case 'download' :
30007                 this.download(e);
30008                 break;
30009             default :
30010                 break;
30011         }
30012         
30013         this.fireEvent('footerbuttonclick', this, type);
30014     },
30015     
30016     beforeSelectFile : function(e)
30017     {
30018         e.preventDefault();
30019         
30020         if(this.fireEvent('beforeselectfile', this) != false){
30021             this.selectorEl.dom.click();
30022         }
30023     },
30024     
30025     onFileSelected : function(e)
30026     {
30027         e.preventDefault();
30028         
30029         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30030             return;
30031         }
30032         
30033         var file = this.selectorEl.dom.files[0];
30034         
30035         if(this.fireEvent('inspect', this, file) != false){
30036             this.prepare(file);
30037         }
30038         
30039     },
30040     
30041     trash : function(e)
30042     {
30043         this.fireEvent('trash', this);
30044     },
30045     
30046     download : function(e)
30047     {
30048         this.fireEvent('download', this);
30049     },
30050     
30051     loadCanvas : function(src)
30052     {   
30053         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30054             
30055             this.reset();
30056             
30057             this.imageEl = document.createElement('img');
30058             
30059             var _this = this;
30060             
30061             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30062             
30063             this.imageEl.src = src;
30064         }
30065     },
30066     
30067     onLoadCanvas : function()
30068     {   
30069         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30070         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30071         
30072         this.bodyEl.un('click', this.beforeSelectFile, this);
30073         
30074         this.notifyEl.hide();
30075         this.thumbEl.show();
30076         this.footerEl.show();
30077         
30078         this.baseRotateLevel();
30079         
30080         if(this.isDocument){
30081             this.setThumbBoxSize();
30082         }
30083         
30084         this.setThumbBoxPosition();
30085         
30086         this.baseScaleLevel();
30087         
30088         this.draw();
30089         
30090         this.resize();
30091         
30092         this.canvasLoaded = true;
30093         
30094         if(this.loadMask){
30095             this.maskEl.unmask();
30096         }
30097         
30098     },
30099     
30100     setCanvasPosition : function()
30101     {   
30102         if(!this.canvasEl){
30103             return;
30104         }
30105         
30106         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30107         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30108         
30109         this.previewEl.setLeft(pw);
30110         this.previewEl.setTop(ph);
30111         
30112     },
30113     
30114     onMouseDown : function(e)
30115     {   
30116         e.stopEvent();
30117         
30118         this.dragable = true;
30119         this.pinching = false;
30120         
30121         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30122             this.dragable = false;
30123             return;
30124         }
30125         
30126         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30127         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30128         
30129     },
30130     
30131     onMouseMove : function(e)
30132     {   
30133         e.stopEvent();
30134         
30135         if(!this.canvasLoaded){
30136             return;
30137         }
30138         
30139         if (!this.dragable){
30140             return;
30141         }
30142         
30143         var minX = Math.ceil(this.thumbEl.getLeft(true));
30144         var minY = Math.ceil(this.thumbEl.getTop(true));
30145         
30146         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30147         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30148         
30149         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30150         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30151         
30152         x = x - this.mouseX;
30153         y = y - this.mouseY;
30154         
30155         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30156         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30157         
30158         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30159         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30160         
30161         this.previewEl.setLeft(bgX);
30162         this.previewEl.setTop(bgY);
30163         
30164         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30165         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30166     },
30167     
30168     onMouseUp : function(e)
30169     {   
30170         e.stopEvent();
30171         
30172         this.dragable = false;
30173     },
30174     
30175     onMouseWheel : function(e)
30176     {   
30177         e.stopEvent();
30178         
30179         this.startScale = this.scale;
30180         
30181         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30182         
30183         if(!this.zoomable()){
30184             this.scale = this.startScale;
30185             return;
30186         }
30187         
30188         this.draw();
30189         
30190         return;
30191     },
30192     
30193     zoomable : function()
30194     {
30195         var minScale = this.thumbEl.getWidth() / this.minWidth;
30196         
30197         if(this.minWidth < this.minHeight){
30198             minScale = this.thumbEl.getHeight() / this.minHeight;
30199         }
30200         
30201         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30202         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30203         
30204         if(
30205                 this.isDocument &&
30206                 (this.rotate == 0 || this.rotate == 180) && 
30207                 (
30208                     width > this.imageEl.OriginWidth || 
30209                     height > this.imageEl.OriginHeight ||
30210                     (width < this.minWidth && height < this.minHeight)
30211                 )
30212         ){
30213             return false;
30214         }
30215         
30216         if(
30217                 this.isDocument &&
30218                 (this.rotate == 90 || this.rotate == 270) && 
30219                 (
30220                     width > this.imageEl.OriginWidth || 
30221                     height > this.imageEl.OriginHeight ||
30222                     (width < this.minHeight && height < this.minWidth)
30223                 )
30224         ){
30225             return false;
30226         }
30227         
30228         if(
30229                 !this.isDocument &&
30230                 (this.rotate == 0 || this.rotate == 180) && 
30231                 (
30232                     width < this.minWidth || 
30233                     width > this.imageEl.OriginWidth || 
30234                     height < this.minHeight || 
30235                     height > this.imageEl.OriginHeight
30236                 )
30237         ){
30238             return false;
30239         }
30240         
30241         if(
30242                 !this.isDocument &&
30243                 (this.rotate == 90 || this.rotate == 270) && 
30244                 (
30245                     width < this.minHeight || 
30246                     width > this.imageEl.OriginWidth || 
30247                     height < this.minWidth || 
30248                     height > this.imageEl.OriginHeight
30249                 )
30250         ){
30251             return false;
30252         }
30253         
30254         return true;
30255         
30256     },
30257     
30258     onRotateLeft : function(e)
30259     {   
30260         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30261             
30262             var minScale = this.thumbEl.getWidth() / this.minWidth;
30263             
30264             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30265             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30266             
30267             this.startScale = this.scale;
30268             
30269             while (this.getScaleLevel() < minScale){
30270             
30271                 this.scale = this.scale + 1;
30272                 
30273                 if(!this.zoomable()){
30274                     break;
30275                 }
30276                 
30277                 if(
30278                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30279                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30280                 ){
30281                     continue;
30282                 }
30283                 
30284                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30285
30286                 this.draw();
30287                 
30288                 return;
30289             }
30290             
30291             this.scale = this.startScale;
30292             
30293             this.onRotateFail();
30294             
30295             return false;
30296         }
30297         
30298         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30299
30300         if(this.isDocument){
30301             this.setThumbBoxSize();
30302             this.setThumbBoxPosition();
30303             this.setCanvasPosition();
30304         }
30305         
30306         this.draw();
30307         
30308         this.fireEvent('rotate', this, 'left');
30309         
30310     },
30311     
30312     onRotateRight : function(e)
30313     {
30314         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30315             
30316             var minScale = this.thumbEl.getWidth() / this.minWidth;
30317         
30318             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30319             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30320             
30321             this.startScale = this.scale;
30322             
30323             while (this.getScaleLevel() < minScale){
30324             
30325                 this.scale = this.scale + 1;
30326                 
30327                 if(!this.zoomable()){
30328                     break;
30329                 }
30330                 
30331                 if(
30332                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30333                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30334                 ){
30335                     continue;
30336                 }
30337                 
30338                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30339
30340                 this.draw();
30341                 
30342                 return;
30343             }
30344             
30345             this.scale = this.startScale;
30346             
30347             this.onRotateFail();
30348             
30349             return false;
30350         }
30351         
30352         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30353
30354         if(this.isDocument){
30355             this.setThumbBoxSize();
30356             this.setThumbBoxPosition();
30357             this.setCanvasPosition();
30358         }
30359         
30360         this.draw();
30361         
30362         this.fireEvent('rotate', this, 'right');
30363     },
30364     
30365     onRotateFail : function()
30366     {
30367         this.errorEl.show(true);
30368         
30369         var _this = this;
30370         
30371         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30372     },
30373     
30374     draw : function()
30375     {
30376         this.previewEl.dom.innerHTML = '';
30377         
30378         var canvasEl = document.createElement("canvas");
30379         
30380         var contextEl = canvasEl.getContext("2d");
30381         
30382         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30383         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30384         var center = this.imageEl.OriginWidth / 2;
30385         
30386         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30387             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30388             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30389             center = this.imageEl.OriginHeight / 2;
30390         }
30391         
30392         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30393         
30394         contextEl.translate(center, center);
30395         contextEl.rotate(this.rotate * Math.PI / 180);
30396
30397         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30398         
30399         this.canvasEl = document.createElement("canvas");
30400         
30401         this.contextEl = this.canvasEl.getContext("2d");
30402         
30403         switch (this.rotate) {
30404             case 0 :
30405                 
30406                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30407                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30408                 
30409                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30410                 
30411                 break;
30412             case 90 : 
30413                 
30414                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30415                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30416                 
30417                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30418                     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);
30419                     break;
30420                 }
30421                 
30422                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30423                 
30424                 break;
30425             case 180 :
30426                 
30427                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30428                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30429                 
30430                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30431                     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);
30432                     break;
30433                 }
30434                 
30435                 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);
30436                 
30437                 break;
30438             case 270 :
30439                 
30440                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30441                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30442         
30443                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30444                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30445                     break;
30446                 }
30447                 
30448                 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);
30449                 
30450                 break;
30451             default : 
30452                 break;
30453         }
30454         
30455         this.previewEl.appendChild(this.canvasEl);
30456         
30457         this.setCanvasPosition();
30458     },
30459     
30460     crop : function()
30461     {
30462         if(!this.canvasLoaded){
30463             return;
30464         }
30465         
30466         var imageCanvas = document.createElement("canvas");
30467         
30468         var imageContext = imageCanvas.getContext("2d");
30469         
30470         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30471         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30472         
30473         var center = imageCanvas.width / 2;
30474         
30475         imageContext.translate(center, center);
30476         
30477         imageContext.rotate(this.rotate * Math.PI / 180);
30478         
30479         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30480         
30481         var canvas = document.createElement("canvas");
30482         
30483         var context = canvas.getContext("2d");
30484                 
30485         canvas.width = this.minWidth;
30486         canvas.height = this.minHeight;
30487
30488         switch (this.rotate) {
30489             case 0 :
30490                 
30491                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30492                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30493                 
30494                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30495                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30496                 
30497                 var targetWidth = this.minWidth - 2 * x;
30498                 var targetHeight = this.minHeight - 2 * y;
30499                 
30500                 var scale = 1;
30501                 
30502                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30503                     scale = targetWidth / width;
30504                 }
30505                 
30506                 if(x > 0 && y == 0){
30507                     scale = targetHeight / height;
30508                 }
30509                 
30510                 if(x > 0 && y > 0){
30511                     scale = targetWidth / width;
30512                     
30513                     if(width < height){
30514                         scale = targetHeight / height;
30515                     }
30516                 }
30517                 
30518                 context.scale(scale, scale);
30519                 
30520                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30521                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30522
30523                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30524                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30525
30526                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30527                 
30528                 break;
30529             case 90 : 
30530                 
30531                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30532                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30533                 
30534                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30535                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30536                 
30537                 var targetWidth = this.minWidth - 2 * x;
30538                 var targetHeight = this.minHeight - 2 * y;
30539                 
30540                 var scale = 1;
30541                 
30542                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30543                     scale = targetWidth / width;
30544                 }
30545                 
30546                 if(x > 0 && y == 0){
30547                     scale = targetHeight / height;
30548                 }
30549                 
30550                 if(x > 0 && y > 0){
30551                     scale = targetWidth / width;
30552                     
30553                     if(width < height){
30554                         scale = targetHeight / height;
30555                     }
30556                 }
30557                 
30558                 context.scale(scale, scale);
30559                 
30560                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30561                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30562
30563                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30564                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30565                 
30566                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30567                 
30568                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30569                 
30570                 break;
30571             case 180 :
30572                 
30573                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30574                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30575                 
30576                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30577                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30578                 
30579                 var targetWidth = this.minWidth - 2 * x;
30580                 var targetHeight = this.minHeight - 2 * y;
30581                 
30582                 var scale = 1;
30583                 
30584                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30585                     scale = targetWidth / width;
30586                 }
30587                 
30588                 if(x > 0 && y == 0){
30589                     scale = targetHeight / height;
30590                 }
30591                 
30592                 if(x > 0 && y > 0){
30593                     scale = targetWidth / width;
30594                     
30595                     if(width < height){
30596                         scale = targetHeight / height;
30597                     }
30598                 }
30599                 
30600                 context.scale(scale, scale);
30601                 
30602                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30603                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30604
30605                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30606                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30607
30608                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30609                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30610                 
30611                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30612                 
30613                 break;
30614             case 270 :
30615                 
30616                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30617                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30618                 
30619                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30620                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30621                 
30622                 var targetWidth = this.minWidth - 2 * x;
30623                 var targetHeight = this.minHeight - 2 * y;
30624                 
30625                 var scale = 1;
30626                 
30627                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30628                     scale = targetWidth / width;
30629                 }
30630                 
30631                 if(x > 0 && y == 0){
30632                     scale = targetHeight / height;
30633                 }
30634                 
30635                 if(x > 0 && y > 0){
30636                     scale = targetWidth / width;
30637                     
30638                     if(width < height){
30639                         scale = targetHeight / height;
30640                     }
30641                 }
30642                 
30643                 context.scale(scale, scale);
30644                 
30645                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30646                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30647
30648                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30649                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30650                 
30651                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30652                 
30653                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30654                 
30655                 break;
30656             default : 
30657                 break;
30658         }
30659         
30660         this.cropData = canvas.toDataURL(this.cropType);
30661         
30662         if(this.fireEvent('crop', this, this.cropData) !== false){
30663             this.process(this.file, this.cropData);
30664         }
30665         
30666         return;
30667         
30668     },
30669     
30670     setThumbBoxSize : function()
30671     {
30672         var width, height;
30673         
30674         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30675             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30676             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30677             
30678             this.minWidth = width;
30679             this.minHeight = height;
30680             
30681             if(this.rotate == 90 || this.rotate == 270){
30682                 this.minWidth = height;
30683                 this.minHeight = width;
30684             }
30685         }
30686         
30687         height = 300;
30688         width = Math.ceil(this.minWidth * height / this.minHeight);
30689         
30690         if(this.minWidth > this.minHeight){
30691             width = 300;
30692             height = Math.ceil(this.minHeight * width / this.minWidth);
30693         }
30694         
30695         this.thumbEl.setStyle({
30696             width : width + 'px',
30697             height : height + 'px'
30698         });
30699
30700         return;
30701             
30702     },
30703     
30704     setThumbBoxPosition : function()
30705     {
30706         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30707         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30708         
30709         this.thumbEl.setLeft(x);
30710         this.thumbEl.setTop(y);
30711         
30712     },
30713     
30714     baseRotateLevel : function()
30715     {
30716         this.baseRotate = 1;
30717         
30718         if(
30719                 typeof(this.exif) != 'undefined' &&
30720                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30721                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30722         ){
30723             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30724         }
30725         
30726         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30727         
30728     },
30729     
30730     baseScaleLevel : function()
30731     {
30732         var width, height;
30733         
30734         if(this.isDocument){
30735             
30736             if(this.baseRotate == 6 || this.baseRotate == 8){
30737             
30738                 height = this.thumbEl.getHeight();
30739                 this.baseScale = height / this.imageEl.OriginWidth;
30740
30741                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30742                     width = this.thumbEl.getWidth();
30743                     this.baseScale = width / this.imageEl.OriginHeight;
30744                 }
30745
30746                 return;
30747             }
30748
30749             height = this.thumbEl.getHeight();
30750             this.baseScale = height / this.imageEl.OriginHeight;
30751
30752             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30753                 width = this.thumbEl.getWidth();
30754                 this.baseScale = width / this.imageEl.OriginWidth;
30755             }
30756
30757             return;
30758         }
30759         
30760         if(this.baseRotate == 6 || this.baseRotate == 8){
30761             
30762             width = this.thumbEl.getHeight();
30763             this.baseScale = width / this.imageEl.OriginHeight;
30764             
30765             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30766                 height = this.thumbEl.getWidth();
30767                 this.baseScale = height / this.imageEl.OriginHeight;
30768             }
30769             
30770             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30771                 height = this.thumbEl.getWidth();
30772                 this.baseScale = height / this.imageEl.OriginHeight;
30773                 
30774                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30775                     width = this.thumbEl.getHeight();
30776                     this.baseScale = width / this.imageEl.OriginWidth;
30777                 }
30778             }
30779             
30780             return;
30781         }
30782         
30783         width = this.thumbEl.getWidth();
30784         this.baseScale = width / this.imageEl.OriginWidth;
30785         
30786         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30787             height = this.thumbEl.getHeight();
30788             this.baseScale = height / this.imageEl.OriginHeight;
30789         }
30790         
30791         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30792             
30793             height = this.thumbEl.getHeight();
30794             this.baseScale = height / this.imageEl.OriginHeight;
30795             
30796             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30797                 width = this.thumbEl.getWidth();
30798                 this.baseScale = width / this.imageEl.OriginWidth;
30799             }
30800             
30801         }
30802         
30803         return;
30804     },
30805     
30806     getScaleLevel : function()
30807     {
30808         return this.baseScale * Math.pow(1.1, this.scale);
30809     },
30810     
30811     onTouchStart : function(e)
30812     {
30813         if(!this.canvasLoaded){
30814             this.beforeSelectFile(e);
30815             return;
30816         }
30817         
30818         var touches = e.browserEvent.touches;
30819         
30820         if(!touches){
30821             return;
30822         }
30823         
30824         if(touches.length == 1){
30825             this.onMouseDown(e);
30826             return;
30827         }
30828         
30829         if(touches.length != 2){
30830             return;
30831         }
30832         
30833         var coords = [];
30834         
30835         for(var i = 0, finger; finger = touches[i]; i++){
30836             coords.push(finger.pageX, finger.pageY);
30837         }
30838         
30839         var x = Math.pow(coords[0] - coords[2], 2);
30840         var y = Math.pow(coords[1] - coords[3], 2);
30841         
30842         this.startDistance = Math.sqrt(x + y);
30843         
30844         this.startScale = this.scale;
30845         
30846         this.pinching = true;
30847         this.dragable = false;
30848         
30849     },
30850     
30851     onTouchMove : function(e)
30852     {
30853         if(!this.pinching && !this.dragable){
30854             return;
30855         }
30856         
30857         var touches = e.browserEvent.touches;
30858         
30859         if(!touches){
30860             return;
30861         }
30862         
30863         if(this.dragable){
30864             this.onMouseMove(e);
30865             return;
30866         }
30867         
30868         var coords = [];
30869         
30870         for(var i = 0, finger; finger = touches[i]; i++){
30871             coords.push(finger.pageX, finger.pageY);
30872         }
30873         
30874         var x = Math.pow(coords[0] - coords[2], 2);
30875         var y = Math.pow(coords[1] - coords[3], 2);
30876         
30877         this.endDistance = Math.sqrt(x + y);
30878         
30879         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30880         
30881         if(!this.zoomable()){
30882             this.scale = this.startScale;
30883             return;
30884         }
30885         
30886         this.draw();
30887         
30888     },
30889     
30890     onTouchEnd : function(e)
30891     {
30892         this.pinching = false;
30893         this.dragable = false;
30894         
30895     },
30896     
30897     process : function(file, crop)
30898     {
30899         if(this.loadMask){
30900             this.maskEl.mask(this.loadingText);
30901         }
30902         
30903         this.xhr = new XMLHttpRequest();
30904         
30905         file.xhr = this.xhr;
30906
30907         this.xhr.open(this.method, this.url, true);
30908         
30909         var headers = {
30910             "Accept": "application/json",
30911             "Cache-Control": "no-cache",
30912             "X-Requested-With": "XMLHttpRequest"
30913         };
30914         
30915         for (var headerName in headers) {
30916             var headerValue = headers[headerName];
30917             if (headerValue) {
30918                 this.xhr.setRequestHeader(headerName, headerValue);
30919             }
30920         }
30921         
30922         var _this = this;
30923         
30924         this.xhr.onload = function()
30925         {
30926             _this.xhrOnLoad(_this.xhr);
30927         }
30928         
30929         this.xhr.onerror = function()
30930         {
30931             _this.xhrOnError(_this.xhr);
30932         }
30933         
30934         var formData = new FormData();
30935
30936         formData.append('returnHTML', 'NO');
30937         
30938         if(crop){
30939             formData.append('crop', crop);
30940         }
30941         
30942         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30943             formData.append(this.paramName, file, file.name);
30944         }
30945         
30946         if(typeof(file.filename) != 'undefined'){
30947             formData.append('filename', file.filename);
30948         }
30949         
30950         if(typeof(file.mimetype) != 'undefined'){
30951             formData.append('mimetype', file.mimetype);
30952         }
30953         
30954         if(this.fireEvent('arrange', this, formData) != false){
30955             this.xhr.send(formData);
30956         };
30957     },
30958     
30959     xhrOnLoad : function(xhr)
30960     {
30961         if(this.loadMask){
30962             this.maskEl.unmask();
30963         }
30964         
30965         if (xhr.readyState !== 4) {
30966             this.fireEvent('exception', this, xhr);
30967             return;
30968         }
30969
30970         var response = Roo.decode(xhr.responseText);
30971         
30972         if(!response.success){
30973             this.fireEvent('exception', this, xhr);
30974             return;
30975         }
30976         
30977         var response = Roo.decode(xhr.responseText);
30978         
30979         this.fireEvent('upload', this, response);
30980         
30981     },
30982     
30983     xhrOnError : function()
30984     {
30985         if(this.loadMask){
30986             this.maskEl.unmask();
30987         }
30988         
30989         Roo.log('xhr on error');
30990         
30991         var response = Roo.decode(xhr.responseText);
30992           
30993         Roo.log(response);
30994         
30995     },
30996     
30997     prepare : function(file)
30998     {   
30999         if(this.loadMask){
31000             this.maskEl.mask(this.loadingText);
31001         }
31002         
31003         this.file = false;
31004         this.exif = {};
31005         
31006         if(typeof(file) === 'string'){
31007             this.loadCanvas(file);
31008             return;
31009         }
31010         
31011         if(!file || !this.urlAPI){
31012             return;
31013         }
31014         
31015         this.file = file;
31016         this.cropType = file.type;
31017         
31018         var _this = this;
31019         
31020         if(this.fireEvent('prepare', this, this.file) != false){
31021             
31022             var reader = new FileReader();
31023             
31024             reader.onload = function (e) {
31025                 if (e.target.error) {
31026                     Roo.log(e.target.error);
31027                     return;
31028                 }
31029                 
31030                 var buffer = e.target.result,
31031                     dataView = new DataView(buffer),
31032                     offset = 2,
31033                     maxOffset = dataView.byteLength - 4,
31034                     markerBytes,
31035                     markerLength;
31036                 
31037                 if (dataView.getUint16(0) === 0xffd8) {
31038                     while (offset < maxOffset) {
31039                         markerBytes = dataView.getUint16(offset);
31040                         
31041                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31042                             markerLength = dataView.getUint16(offset + 2) + 2;
31043                             if (offset + markerLength > dataView.byteLength) {
31044                                 Roo.log('Invalid meta data: Invalid segment size.');
31045                                 break;
31046                             }
31047                             
31048                             if(markerBytes == 0xffe1){
31049                                 _this.parseExifData(
31050                                     dataView,
31051                                     offset,
31052                                     markerLength
31053                                 );
31054                             }
31055                             
31056                             offset += markerLength;
31057                             
31058                             continue;
31059                         }
31060                         
31061                         break;
31062                     }
31063                     
31064                 }
31065                 
31066                 var url = _this.urlAPI.createObjectURL(_this.file);
31067                 
31068                 _this.loadCanvas(url);
31069                 
31070                 return;
31071             }
31072             
31073             reader.readAsArrayBuffer(this.file);
31074             
31075         }
31076         
31077     },
31078     
31079     parseExifData : function(dataView, offset, length)
31080     {
31081         var tiffOffset = offset + 10,
31082             littleEndian,
31083             dirOffset;
31084     
31085         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31086             // No Exif data, might be XMP data instead
31087             return;
31088         }
31089         
31090         // Check for the ASCII code for "Exif" (0x45786966):
31091         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31092             // No Exif data, might be XMP data instead
31093             return;
31094         }
31095         if (tiffOffset + 8 > dataView.byteLength) {
31096             Roo.log('Invalid Exif data: Invalid segment size.');
31097             return;
31098         }
31099         // Check for the two null bytes:
31100         if (dataView.getUint16(offset + 8) !== 0x0000) {
31101             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31102             return;
31103         }
31104         // Check the byte alignment:
31105         switch (dataView.getUint16(tiffOffset)) {
31106         case 0x4949:
31107             littleEndian = true;
31108             break;
31109         case 0x4D4D:
31110             littleEndian = false;
31111             break;
31112         default:
31113             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31114             return;
31115         }
31116         // Check for the TIFF tag marker (0x002A):
31117         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31118             Roo.log('Invalid Exif data: Missing TIFF marker.');
31119             return;
31120         }
31121         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31122         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31123         
31124         this.parseExifTags(
31125             dataView,
31126             tiffOffset,
31127             tiffOffset + dirOffset,
31128             littleEndian
31129         );
31130     },
31131     
31132     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31133     {
31134         var tagsNumber,
31135             dirEndOffset,
31136             i;
31137         if (dirOffset + 6 > dataView.byteLength) {
31138             Roo.log('Invalid Exif data: Invalid directory offset.');
31139             return;
31140         }
31141         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31142         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31143         if (dirEndOffset + 4 > dataView.byteLength) {
31144             Roo.log('Invalid Exif data: Invalid directory size.');
31145             return;
31146         }
31147         for (i = 0; i < tagsNumber; i += 1) {
31148             this.parseExifTag(
31149                 dataView,
31150                 tiffOffset,
31151                 dirOffset + 2 + 12 * i, // tag offset
31152                 littleEndian
31153             );
31154         }
31155         // Return the offset to the next directory:
31156         return dataView.getUint32(dirEndOffset, littleEndian);
31157     },
31158     
31159     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31160     {
31161         var tag = dataView.getUint16(offset, littleEndian);
31162         
31163         this.exif[tag] = this.getExifValue(
31164             dataView,
31165             tiffOffset,
31166             offset,
31167             dataView.getUint16(offset + 2, littleEndian), // tag type
31168             dataView.getUint32(offset + 4, littleEndian), // tag length
31169             littleEndian
31170         );
31171     },
31172     
31173     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31174     {
31175         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31176             tagSize,
31177             dataOffset,
31178             values,
31179             i,
31180             str,
31181             c;
31182     
31183         if (!tagType) {
31184             Roo.log('Invalid Exif data: Invalid tag type.');
31185             return;
31186         }
31187         
31188         tagSize = tagType.size * length;
31189         // Determine if the value is contained in the dataOffset bytes,
31190         // or if the value at the dataOffset is a pointer to the actual data:
31191         dataOffset = tagSize > 4 ?
31192                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31193         if (dataOffset + tagSize > dataView.byteLength) {
31194             Roo.log('Invalid Exif data: Invalid data offset.');
31195             return;
31196         }
31197         if (length === 1) {
31198             return tagType.getValue(dataView, dataOffset, littleEndian);
31199         }
31200         values = [];
31201         for (i = 0; i < length; i += 1) {
31202             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31203         }
31204         
31205         if (tagType.ascii) {
31206             str = '';
31207             // Concatenate the chars:
31208             for (i = 0; i < values.length; i += 1) {
31209                 c = values[i];
31210                 // Ignore the terminating NULL byte(s):
31211                 if (c === '\u0000') {
31212                     break;
31213                 }
31214                 str += c;
31215             }
31216             return str;
31217         }
31218         return values;
31219     }
31220     
31221 });
31222
31223 Roo.apply(Roo.bootstrap.UploadCropbox, {
31224     tags : {
31225         'Orientation': 0x0112
31226     },
31227     
31228     Orientation: {
31229             1: 0, //'top-left',
31230 //            2: 'top-right',
31231             3: 180, //'bottom-right',
31232 //            4: 'bottom-left',
31233 //            5: 'left-top',
31234             6: 90, //'right-top',
31235 //            7: 'right-bottom',
31236             8: 270 //'left-bottom'
31237     },
31238     
31239     exifTagTypes : {
31240         // byte, 8-bit unsigned int:
31241         1: {
31242             getValue: function (dataView, dataOffset) {
31243                 return dataView.getUint8(dataOffset);
31244             },
31245             size: 1
31246         },
31247         // ascii, 8-bit byte:
31248         2: {
31249             getValue: function (dataView, dataOffset) {
31250                 return String.fromCharCode(dataView.getUint8(dataOffset));
31251             },
31252             size: 1,
31253             ascii: true
31254         },
31255         // short, 16 bit int:
31256         3: {
31257             getValue: function (dataView, dataOffset, littleEndian) {
31258                 return dataView.getUint16(dataOffset, littleEndian);
31259             },
31260             size: 2
31261         },
31262         // long, 32 bit int:
31263         4: {
31264             getValue: function (dataView, dataOffset, littleEndian) {
31265                 return dataView.getUint32(dataOffset, littleEndian);
31266             },
31267             size: 4
31268         },
31269         // rational = two long values, first is numerator, second is denominator:
31270         5: {
31271             getValue: function (dataView, dataOffset, littleEndian) {
31272                 return dataView.getUint32(dataOffset, littleEndian) /
31273                     dataView.getUint32(dataOffset + 4, littleEndian);
31274             },
31275             size: 8
31276         },
31277         // slong, 32 bit signed int:
31278         9: {
31279             getValue: function (dataView, dataOffset, littleEndian) {
31280                 return dataView.getInt32(dataOffset, littleEndian);
31281             },
31282             size: 4
31283         },
31284         // srational, two slongs, first is numerator, second is denominator:
31285         10: {
31286             getValue: function (dataView, dataOffset, littleEndian) {
31287                 return dataView.getInt32(dataOffset, littleEndian) /
31288                     dataView.getInt32(dataOffset + 4, littleEndian);
31289             },
31290             size: 8
31291         }
31292     },
31293     
31294     footer : {
31295         STANDARD : [
31296             {
31297                 tag : 'div',
31298                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31299                 action : 'rotate-left',
31300                 cn : [
31301                     {
31302                         tag : 'button',
31303                         cls : 'btn btn-default',
31304                         html : '<i class="fa fa-undo"></i>'
31305                     }
31306                 ]
31307             },
31308             {
31309                 tag : 'div',
31310                 cls : 'btn-group roo-upload-cropbox-picture',
31311                 action : 'picture',
31312                 cn : [
31313                     {
31314                         tag : 'button',
31315                         cls : 'btn btn-default',
31316                         html : '<i class="fa fa-picture-o"></i>'
31317                     }
31318                 ]
31319             },
31320             {
31321                 tag : 'div',
31322                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31323                 action : 'rotate-right',
31324                 cn : [
31325                     {
31326                         tag : 'button',
31327                         cls : 'btn btn-default',
31328                         html : '<i class="fa fa-repeat"></i>'
31329                     }
31330                 ]
31331             }
31332         ],
31333         DOCUMENT : [
31334             {
31335                 tag : 'div',
31336                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31337                 action : 'rotate-left',
31338                 cn : [
31339                     {
31340                         tag : 'button',
31341                         cls : 'btn btn-default',
31342                         html : '<i class="fa fa-undo"></i>'
31343                     }
31344                 ]
31345             },
31346             {
31347                 tag : 'div',
31348                 cls : 'btn-group roo-upload-cropbox-download',
31349                 action : 'download',
31350                 cn : [
31351                     {
31352                         tag : 'button',
31353                         cls : 'btn btn-default',
31354                         html : '<i class="fa fa-download"></i>'
31355                     }
31356                 ]
31357             },
31358             {
31359                 tag : 'div',
31360                 cls : 'btn-group roo-upload-cropbox-crop',
31361                 action : 'crop',
31362                 cn : [
31363                     {
31364                         tag : 'button',
31365                         cls : 'btn btn-default',
31366                         html : '<i class="fa fa-crop"></i>'
31367                     }
31368                 ]
31369             },
31370             {
31371                 tag : 'div',
31372                 cls : 'btn-group roo-upload-cropbox-trash',
31373                 action : 'trash',
31374                 cn : [
31375                     {
31376                         tag : 'button',
31377                         cls : 'btn btn-default',
31378                         html : '<i class="fa fa-trash"></i>'
31379                     }
31380                 ]
31381             },
31382             {
31383                 tag : 'div',
31384                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31385                 action : 'rotate-right',
31386                 cn : [
31387                     {
31388                         tag : 'button',
31389                         cls : 'btn btn-default',
31390                         html : '<i class="fa fa-repeat"></i>'
31391                     }
31392                 ]
31393             }
31394         ],
31395         ROTATOR : [
31396             {
31397                 tag : 'div',
31398                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31399                 action : 'rotate-left',
31400                 cn : [
31401                     {
31402                         tag : 'button',
31403                         cls : 'btn btn-default',
31404                         html : '<i class="fa fa-undo"></i>'
31405                     }
31406                 ]
31407             },
31408             {
31409                 tag : 'div',
31410                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31411                 action : 'rotate-right',
31412                 cn : [
31413                     {
31414                         tag : 'button',
31415                         cls : 'btn btn-default',
31416                         html : '<i class="fa fa-repeat"></i>'
31417                     }
31418                 ]
31419             }
31420         ]
31421     }
31422 });
31423
31424 /*
31425 * Licence: LGPL
31426 */
31427
31428 /**
31429  * @class Roo.bootstrap.DocumentManager
31430  * @extends Roo.bootstrap.Component
31431  * Bootstrap DocumentManager class
31432  * @cfg {String} paramName default 'imageUpload'
31433  * @cfg {String} toolTipName default 'filename'
31434  * @cfg {String} method default POST
31435  * @cfg {String} url action url
31436  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31437  * @cfg {Boolean} multiple multiple upload default true
31438  * @cfg {Number} thumbSize default 300
31439  * @cfg {String} fieldLabel
31440  * @cfg {Number} labelWidth default 4
31441  * @cfg {String} labelAlign (left|top) default left
31442  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31443 * @cfg {Number} labellg set the width of label (1-12)
31444  * @cfg {Number} labelmd set the width of label (1-12)
31445  * @cfg {Number} labelsm set the width of label (1-12)
31446  * @cfg {Number} labelxs set the width of label (1-12)
31447  * 
31448  * @constructor
31449  * Create a new DocumentManager
31450  * @param {Object} config The config object
31451  */
31452
31453 Roo.bootstrap.DocumentManager = function(config){
31454     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31455     
31456     this.files = [];
31457     this.delegates = [];
31458     
31459     this.addEvents({
31460         /**
31461          * @event initial
31462          * Fire when initial the DocumentManager
31463          * @param {Roo.bootstrap.DocumentManager} this
31464          */
31465         "initial" : true,
31466         /**
31467          * @event inspect
31468          * inspect selected file
31469          * @param {Roo.bootstrap.DocumentManager} this
31470          * @param {File} file
31471          */
31472         "inspect" : true,
31473         /**
31474          * @event exception
31475          * Fire when xhr load exception
31476          * @param {Roo.bootstrap.DocumentManager} this
31477          * @param {XMLHttpRequest} xhr
31478          */
31479         "exception" : true,
31480         /**
31481          * @event afterupload
31482          * Fire when xhr load exception
31483          * @param {Roo.bootstrap.DocumentManager} this
31484          * @param {XMLHttpRequest} xhr
31485          */
31486         "afterupload" : true,
31487         /**
31488          * @event prepare
31489          * prepare the form data
31490          * @param {Roo.bootstrap.DocumentManager} this
31491          * @param {Object} formData
31492          */
31493         "prepare" : true,
31494         /**
31495          * @event remove
31496          * Fire when remove the file
31497          * @param {Roo.bootstrap.DocumentManager} this
31498          * @param {Object} file
31499          */
31500         "remove" : true,
31501         /**
31502          * @event refresh
31503          * Fire after refresh the file
31504          * @param {Roo.bootstrap.DocumentManager} this
31505          */
31506         "refresh" : true,
31507         /**
31508          * @event click
31509          * Fire after click the image
31510          * @param {Roo.bootstrap.DocumentManager} this
31511          * @param {Object} file
31512          */
31513         "click" : true,
31514         /**
31515          * @event edit
31516          * Fire when upload a image and editable set to true
31517          * @param {Roo.bootstrap.DocumentManager} this
31518          * @param {Object} file
31519          */
31520         "edit" : true,
31521         /**
31522          * @event beforeselectfile
31523          * Fire before select file
31524          * @param {Roo.bootstrap.DocumentManager} this
31525          */
31526         "beforeselectfile" : true,
31527         /**
31528          * @event process
31529          * Fire before process file
31530          * @param {Roo.bootstrap.DocumentManager} this
31531          * @param {Object} file
31532          */
31533         "process" : true,
31534         /**
31535          * @event previewrendered
31536          * Fire when preview rendered
31537          * @param {Roo.bootstrap.DocumentManager} this
31538          * @param {Object} file
31539          */
31540         "previewrendered" : true,
31541         /**
31542          */
31543         "previewResize" : true
31544         
31545     });
31546 };
31547
31548 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31549     
31550     boxes : 0,
31551     inputName : '',
31552     thumbSize : 300,
31553     multiple : true,
31554     files : false,
31555     method : 'POST',
31556     url : '',
31557     paramName : 'imageUpload',
31558     toolTipName : 'filename',
31559     fieldLabel : '',
31560     labelWidth : 4,
31561     labelAlign : 'left',
31562     editable : true,
31563     delegates : false,
31564     xhr : false, 
31565     
31566     labellg : 0,
31567     labelmd : 0,
31568     labelsm : 0,
31569     labelxs : 0,
31570     
31571     getAutoCreate : function()
31572     {   
31573         var managerWidget = {
31574             tag : 'div',
31575             cls : 'roo-document-manager',
31576             cn : [
31577                 {
31578                     tag : 'input',
31579                     cls : 'roo-document-manager-selector',
31580                     type : 'file'
31581                 },
31582                 {
31583                     tag : 'div',
31584                     cls : 'roo-document-manager-uploader',
31585                     cn : [
31586                         {
31587                             tag : 'div',
31588                             cls : 'roo-document-manager-upload-btn',
31589                             html : '<i class="fa fa-plus"></i>'
31590                         }
31591                     ]
31592                     
31593                 }
31594             ]
31595         };
31596         
31597         var content = [
31598             {
31599                 tag : 'div',
31600                 cls : 'column col-md-12',
31601                 cn : managerWidget
31602             }
31603         ];
31604         
31605         if(this.fieldLabel.length){
31606             
31607             content = [
31608                 {
31609                     tag : 'div',
31610                     cls : 'column col-md-12',
31611                     html : this.fieldLabel
31612                 },
31613                 {
31614                     tag : 'div',
31615                     cls : 'column col-md-12',
31616                     cn : managerWidget
31617                 }
31618             ];
31619
31620             if(this.labelAlign == 'left'){
31621                 content = [
31622                     {
31623                         tag : 'div',
31624                         cls : 'column',
31625                         html : this.fieldLabel
31626                     },
31627                     {
31628                         tag : 'div',
31629                         cls : 'column',
31630                         cn : managerWidget
31631                     }
31632                 ];
31633                 
31634                 if(this.labelWidth > 12){
31635                     content[0].style = "width: " + this.labelWidth + 'px';
31636                 }
31637
31638                 if(this.labelWidth < 13 && this.labelmd == 0){
31639                     this.labelmd = this.labelWidth;
31640                 }
31641
31642                 if(this.labellg > 0){
31643                     content[0].cls += ' col-lg-' + this.labellg;
31644                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31645                 }
31646
31647                 if(this.labelmd > 0){
31648                     content[0].cls += ' col-md-' + this.labelmd;
31649                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31650                 }
31651
31652                 if(this.labelsm > 0){
31653                     content[0].cls += ' col-sm-' + this.labelsm;
31654                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31655                 }
31656
31657                 if(this.labelxs > 0){
31658                     content[0].cls += ' col-xs-' + this.labelxs;
31659                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31660                 }
31661                 
31662             }
31663         }
31664         
31665         var cfg = {
31666             tag : 'div',
31667             cls : 'row clearfix',
31668             cn : content
31669         };
31670         
31671         return cfg;
31672         
31673     },
31674     
31675     initEvents : function()
31676     {
31677         this.managerEl = this.el.select('.roo-document-manager', true).first();
31678         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31679         
31680         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31681         this.selectorEl.hide();
31682         
31683         if(this.multiple){
31684             this.selectorEl.attr('multiple', 'multiple');
31685         }
31686         
31687         this.selectorEl.on('change', this.onFileSelected, this);
31688         
31689         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31690         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31691         
31692         this.uploader.on('click', this.onUploaderClick, this);
31693         
31694         this.renderProgressDialog();
31695         
31696         var _this = this;
31697         
31698         window.addEventListener("resize", function() { _this.refresh(); } );
31699         
31700         this.fireEvent('initial', this);
31701     },
31702     
31703     renderProgressDialog : function()
31704     {
31705         var _this = this;
31706         
31707         this.progressDialog = new Roo.bootstrap.Modal({
31708             cls : 'roo-document-manager-progress-dialog',
31709             allow_close : false,
31710             animate : false,
31711             title : '',
31712             buttons : [
31713                 {
31714                     name  :'cancel',
31715                     weight : 'danger',
31716                     html : 'Cancel'
31717                 }
31718             ], 
31719             listeners : { 
31720                 btnclick : function() {
31721                     _this.uploadCancel();
31722                     this.hide();
31723                 }
31724             }
31725         });
31726          
31727         this.progressDialog.render(Roo.get(document.body));
31728          
31729         this.progress = new Roo.bootstrap.Progress({
31730             cls : 'roo-document-manager-progress',
31731             active : true,
31732             striped : true
31733         });
31734         
31735         this.progress.render(this.progressDialog.getChildContainer());
31736         
31737         this.progressBar = new Roo.bootstrap.ProgressBar({
31738             cls : 'roo-document-manager-progress-bar',
31739             aria_valuenow : 0,
31740             aria_valuemin : 0,
31741             aria_valuemax : 12,
31742             panel : 'success'
31743         });
31744         
31745         this.progressBar.render(this.progress.getChildContainer());
31746     },
31747     
31748     onUploaderClick : function(e)
31749     {
31750         e.preventDefault();
31751      
31752         if(this.fireEvent('beforeselectfile', this) != false){
31753             this.selectorEl.dom.click();
31754         }
31755         
31756     },
31757     
31758     onFileSelected : function(e)
31759     {
31760         e.preventDefault();
31761         
31762         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31763             return;
31764         }
31765         
31766         Roo.each(this.selectorEl.dom.files, function(file){
31767             if(this.fireEvent('inspect', this, file) != false){
31768                 this.files.push(file);
31769             }
31770         }, this);
31771         
31772         this.queue();
31773         
31774     },
31775     
31776     queue : function()
31777     {
31778         this.selectorEl.dom.value = '';
31779         
31780         if(!this.files || !this.files.length){
31781             return;
31782         }
31783         
31784         if(this.boxes > 0 && this.files.length > this.boxes){
31785             this.files = this.files.slice(0, this.boxes);
31786         }
31787         
31788         this.uploader.show();
31789         
31790         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31791             this.uploader.hide();
31792         }
31793         
31794         var _this = this;
31795         
31796         var files = [];
31797         
31798         var docs = [];
31799         
31800         Roo.each(this.files, function(file){
31801             
31802             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31803                 var f = this.renderPreview(file);
31804                 files.push(f);
31805                 return;
31806             }
31807             
31808             if(file.type.indexOf('image') != -1){
31809                 this.delegates.push(
31810                     (function(){
31811                         _this.process(file);
31812                     }).createDelegate(this)
31813                 );
31814         
31815                 return;
31816             }
31817             
31818             docs.push(
31819                 (function(){
31820                     _this.process(file);
31821                 }).createDelegate(this)
31822             );
31823             
31824         }, this);
31825         
31826         this.files = files;
31827         
31828         this.delegates = this.delegates.concat(docs);
31829         
31830         if(!this.delegates.length){
31831             this.refresh();
31832             return;
31833         }
31834         
31835         this.progressBar.aria_valuemax = this.delegates.length;
31836         
31837         this.arrange();
31838         
31839         return;
31840     },
31841     
31842     arrange : function()
31843     {
31844         if(!this.delegates.length){
31845             this.progressDialog.hide();
31846             this.refresh();
31847             return;
31848         }
31849         
31850         var delegate = this.delegates.shift();
31851         
31852         this.progressDialog.show();
31853         
31854         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31855         
31856         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31857         
31858         delegate();
31859     },
31860     
31861     refresh : function()
31862     {
31863         this.uploader.show();
31864         
31865         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31866             this.uploader.hide();
31867         }
31868         
31869         Roo.isTouch ? this.closable(false) : this.closable(true);
31870         
31871         this.fireEvent('refresh', this);
31872     },
31873     
31874     onRemove : function(e, el, o)
31875     {
31876         e.preventDefault();
31877         
31878         this.fireEvent('remove', this, o);
31879         
31880     },
31881     
31882     remove : function(o)
31883     {
31884         var files = [];
31885         
31886         Roo.each(this.files, function(file){
31887             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31888                 files.push(file);
31889                 return;
31890             }
31891
31892             o.target.remove();
31893
31894         }, this);
31895         
31896         this.files = files;
31897         
31898         this.refresh();
31899     },
31900     
31901     clear : function()
31902     {
31903         Roo.each(this.files, function(file){
31904             if(!file.target){
31905                 return;
31906             }
31907             
31908             file.target.remove();
31909
31910         }, this);
31911         
31912         this.files = [];
31913         
31914         this.refresh();
31915     },
31916     
31917     onClick : function(e, el, o)
31918     {
31919         e.preventDefault();
31920         
31921         this.fireEvent('click', this, o);
31922         
31923     },
31924     
31925     closable : function(closable)
31926     {
31927         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31928             
31929             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31930             
31931             if(closable){
31932                 el.show();
31933                 return;
31934             }
31935             
31936             el.hide();
31937             
31938         }, this);
31939     },
31940     
31941     xhrOnLoad : function(xhr)
31942     {
31943         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31944             el.remove();
31945         }, this);
31946         
31947         if (xhr.readyState !== 4) {
31948             this.arrange();
31949             this.fireEvent('exception', this, xhr);
31950             return;
31951         }
31952
31953         var response = Roo.decode(xhr.responseText);
31954         
31955         if(!response.success){
31956             this.arrange();
31957             this.fireEvent('exception', this, xhr);
31958             return;
31959         }
31960         
31961         var file = this.renderPreview(response.data);
31962         
31963         this.files.push(file);
31964         
31965         this.arrange();
31966         
31967         this.fireEvent('afterupload', this, xhr);
31968         
31969     },
31970     
31971     xhrOnError : function(xhr)
31972     {
31973         Roo.log('xhr on error');
31974         
31975         var response = Roo.decode(xhr.responseText);
31976           
31977         Roo.log(response);
31978         
31979         this.arrange();
31980     },
31981     
31982     process : function(file)
31983     {
31984         if(this.fireEvent('process', this, file) !== false){
31985             if(this.editable && file.type.indexOf('image') != -1){
31986                 this.fireEvent('edit', this, file);
31987                 return;
31988             }
31989
31990             this.uploadStart(file, false);
31991
31992             return;
31993         }
31994         
31995     },
31996     
31997     uploadStart : function(file, crop)
31998     {
31999         this.xhr = new XMLHttpRequest();
32000         
32001         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32002             this.arrange();
32003             return;
32004         }
32005         
32006         file.xhr = this.xhr;
32007             
32008         this.managerEl.createChild({
32009             tag : 'div',
32010             cls : 'roo-document-manager-loading',
32011             cn : [
32012                 {
32013                     tag : 'div',
32014                     tooltip : file.name,
32015                     cls : 'roo-document-manager-thumb',
32016                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32017                 }
32018             ]
32019
32020         });
32021
32022         this.xhr.open(this.method, this.url, true);
32023         
32024         var headers = {
32025             "Accept": "application/json",
32026             "Cache-Control": "no-cache",
32027             "X-Requested-With": "XMLHttpRequest"
32028         };
32029         
32030         for (var headerName in headers) {
32031             var headerValue = headers[headerName];
32032             if (headerValue) {
32033                 this.xhr.setRequestHeader(headerName, headerValue);
32034             }
32035         }
32036         
32037         var _this = this;
32038         
32039         this.xhr.onload = function()
32040         {
32041             _this.xhrOnLoad(_this.xhr);
32042         }
32043         
32044         this.xhr.onerror = function()
32045         {
32046             _this.xhrOnError(_this.xhr);
32047         }
32048         
32049         var formData = new FormData();
32050
32051         formData.append('returnHTML', 'NO');
32052         
32053         if(crop){
32054             formData.append('crop', crop);
32055         }
32056         
32057         formData.append(this.paramName, file, file.name);
32058         
32059         var options = {
32060             file : file, 
32061             manually : false
32062         };
32063         
32064         if(this.fireEvent('prepare', this, formData, options) != false){
32065             
32066             if(options.manually){
32067                 return;
32068             }
32069             
32070             this.xhr.send(formData);
32071             return;
32072         };
32073         
32074         this.uploadCancel();
32075     },
32076     
32077     uploadCancel : function()
32078     {
32079         if (this.xhr) {
32080             this.xhr.abort();
32081         }
32082         
32083         this.delegates = [];
32084         
32085         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32086             el.remove();
32087         }, this);
32088         
32089         this.arrange();
32090     },
32091     
32092     renderPreview : function(file)
32093     {
32094         if(typeof(file.target) != 'undefined' && file.target){
32095             return file;
32096         }
32097         
32098         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32099         
32100         var previewEl = this.managerEl.createChild({
32101             tag : 'div',
32102             cls : 'roo-document-manager-preview',
32103             cn : [
32104                 {
32105                     tag : 'div',
32106                     tooltip : file[this.toolTipName],
32107                     cls : 'roo-document-manager-thumb',
32108                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32109                 },
32110                 {
32111                     tag : 'button',
32112                     cls : 'close',
32113                     html : '<i class="fa fa-times-circle"></i>'
32114                 }
32115             ]
32116         });
32117
32118         var close = previewEl.select('button.close', true).first();
32119
32120         close.on('click', this.onRemove, this, file);
32121
32122         file.target = previewEl;
32123
32124         var image = previewEl.select('img', true).first();
32125         
32126         var _this = this;
32127         
32128         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32129         
32130         image.on('click', this.onClick, this, file);
32131         
32132         this.fireEvent('previewrendered', this, file);
32133         
32134         return file;
32135         
32136     },
32137     
32138     onPreviewLoad : function(file, image)
32139     {
32140         if(typeof(file.target) == 'undefined' || !file.target){
32141             return;
32142         }
32143         
32144         var width = image.dom.naturalWidth || image.dom.width;
32145         var height = image.dom.naturalHeight || image.dom.height;
32146         
32147         if(!this.previewResize) {
32148             return;
32149         }
32150         
32151         if(width > height){
32152             file.target.addClass('wide');
32153             return;
32154         }
32155         
32156         file.target.addClass('tall');
32157         return;
32158         
32159     },
32160     
32161     uploadFromSource : function(file, crop)
32162     {
32163         this.xhr = new XMLHttpRequest();
32164         
32165         this.managerEl.createChild({
32166             tag : 'div',
32167             cls : 'roo-document-manager-loading',
32168             cn : [
32169                 {
32170                     tag : 'div',
32171                     tooltip : file.name,
32172                     cls : 'roo-document-manager-thumb',
32173                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32174                 }
32175             ]
32176
32177         });
32178
32179         this.xhr.open(this.method, this.url, true);
32180         
32181         var headers = {
32182             "Accept": "application/json",
32183             "Cache-Control": "no-cache",
32184             "X-Requested-With": "XMLHttpRequest"
32185         };
32186         
32187         for (var headerName in headers) {
32188             var headerValue = headers[headerName];
32189             if (headerValue) {
32190                 this.xhr.setRequestHeader(headerName, headerValue);
32191             }
32192         }
32193         
32194         var _this = this;
32195         
32196         this.xhr.onload = function()
32197         {
32198             _this.xhrOnLoad(_this.xhr);
32199         }
32200         
32201         this.xhr.onerror = function()
32202         {
32203             _this.xhrOnError(_this.xhr);
32204         }
32205         
32206         var formData = new FormData();
32207
32208         formData.append('returnHTML', 'NO');
32209         
32210         formData.append('crop', crop);
32211         
32212         if(typeof(file.filename) != 'undefined'){
32213             formData.append('filename', file.filename);
32214         }
32215         
32216         if(typeof(file.mimetype) != 'undefined'){
32217             formData.append('mimetype', file.mimetype);
32218         }
32219         
32220         Roo.log(formData);
32221         
32222         if(this.fireEvent('prepare', this, formData) != false){
32223             this.xhr.send(formData);
32224         };
32225     }
32226 });
32227
32228 /*
32229 * Licence: LGPL
32230 */
32231
32232 /**
32233  * @class Roo.bootstrap.DocumentViewer
32234  * @extends Roo.bootstrap.Component
32235  * Bootstrap DocumentViewer class
32236  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32237  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32238  * 
32239  * @constructor
32240  * Create a new DocumentViewer
32241  * @param {Object} config The config object
32242  */
32243
32244 Roo.bootstrap.DocumentViewer = function(config){
32245     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32246     
32247     this.addEvents({
32248         /**
32249          * @event initial
32250          * Fire after initEvent
32251          * @param {Roo.bootstrap.DocumentViewer} this
32252          */
32253         "initial" : true,
32254         /**
32255          * @event click
32256          * Fire after click
32257          * @param {Roo.bootstrap.DocumentViewer} this
32258          */
32259         "click" : true,
32260         /**
32261          * @event download
32262          * Fire after download button
32263          * @param {Roo.bootstrap.DocumentViewer} this
32264          */
32265         "download" : true,
32266         /**
32267          * @event trash
32268          * Fire after trash button
32269          * @param {Roo.bootstrap.DocumentViewer} this
32270          */
32271         "trash" : true
32272         
32273     });
32274 };
32275
32276 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32277     
32278     showDownload : true,
32279     
32280     showTrash : true,
32281     
32282     getAutoCreate : function()
32283     {
32284         var cfg = {
32285             tag : 'div',
32286             cls : 'roo-document-viewer',
32287             cn : [
32288                 {
32289                     tag : 'div',
32290                     cls : 'roo-document-viewer-body',
32291                     cn : [
32292                         {
32293                             tag : 'div',
32294                             cls : 'roo-document-viewer-thumb',
32295                             cn : [
32296                                 {
32297                                     tag : 'img',
32298                                     cls : 'roo-document-viewer-image'
32299                                 }
32300                             ]
32301                         }
32302                     ]
32303                 },
32304                 {
32305                     tag : 'div',
32306                     cls : 'roo-document-viewer-footer',
32307                     cn : {
32308                         tag : 'div',
32309                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32310                         cn : [
32311                             {
32312                                 tag : 'div',
32313                                 cls : 'btn-group roo-document-viewer-download',
32314                                 cn : [
32315                                     {
32316                                         tag : 'button',
32317                                         cls : 'btn btn-default',
32318                                         html : '<i class="fa fa-download"></i>'
32319                                     }
32320                                 ]
32321                             },
32322                             {
32323                                 tag : 'div',
32324                                 cls : 'btn-group roo-document-viewer-trash',
32325                                 cn : [
32326                                     {
32327                                         tag : 'button',
32328                                         cls : 'btn btn-default',
32329                                         html : '<i class="fa fa-trash"></i>'
32330                                     }
32331                                 ]
32332                             }
32333                         ]
32334                     }
32335                 }
32336             ]
32337         };
32338         
32339         return cfg;
32340     },
32341     
32342     initEvents : function()
32343     {
32344         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32345         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32346         
32347         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32348         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32349         
32350         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32351         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32352         
32353         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32354         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32355         
32356         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32357         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32358         
32359         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32360         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32361         
32362         this.bodyEl.on('click', this.onClick, this);
32363         this.downloadBtn.on('click', this.onDownload, this);
32364         this.trashBtn.on('click', this.onTrash, this);
32365         
32366         this.downloadBtn.hide();
32367         this.trashBtn.hide();
32368         
32369         if(this.showDownload){
32370             this.downloadBtn.show();
32371         }
32372         
32373         if(this.showTrash){
32374             this.trashBtn.show();
32375         }
32376         
32377         if(!this.showDownload && !this.showTrash) {
32378             this.footerEl.hide();
32379         }
32380         
32381     },
32382     
32383     initial : function()
32384     {
32385         this.fireEvent('initial', this);
32386         
32387     },
32388     
32389     onClick : function(e)
32390     {
32391         e.preventDefault();
32392         
32393         this.fireEvent('click', this);
32394     },
32395     
32396     onDownload : function(e)
32397     {
32398         e.preventDefault();
32399         
32400         this.fireEvent('download', this);
32401     },
32402     
32403     onTrash : function(e)
32404     {
32405         e.preventDefault();
32406         
32407         this.fireEvent('trash', this);
32408     }
32409     
32410 });
32411 /*
32412  * - LGPL
32413  *
32414  * nav progress bar
32415  * 
32416  */
32417
32418 /**
32419  * @class Roo.bootstrap.NavProgressBar
32420  * @extends Roo.bootstrap.Component
32421  * Bootstrap NavProgressBar class
32422  * 
32423  * @constructor
32424  * Create a new nav progress bar
32425  * @param {Object} config The config object
32426  */
32427
32428 Roo.bootstrap.NavProgressBar = function(config){
32429     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32430
32431     this.bullets = this.bullets || [];
32432    
32433 //    Roo.bootstrap.NavProgressBar.register(this);
32434      this.addEvents({
32435         /**
32436              * @event changed
32437              * Fires when the active item changes
32438              * @param {Roo.bootstrap.NavProgressBar} this
32439              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32440              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32441          */
32442         'changed': true
32443      });
32444     
32445 };
32446
32447 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32448     
32449     bullets : [],
32450     barItems : [],
32451     
32452     getAutoCreate : function()
32453     {
32454         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32455         
32456         cfg = {
32457             tag : 'div',
32458             cls : 'roo-navigation-bar-group',
32459             cn : [
32460                 {
32461                     tag : 'div',
32462                     cls : 'roo-navigation-top-bar'
32463                 },
32464                 {
32465                     tag : 'div',
32466                     cls : 'roo-navigation-bullets-bar',
32467                     cn : [
32468                         {
32469                             tag : 'ul',
32470                             cls : 'roo-navigation-bar'
32471                         }
32472                     ]
32473                 },
32474                 
32475                 {
32476                     tag : 'div',
32477                     cls : 'roo-navigation-bottom-bar'
32478                 }
32479             ]
32480             
32481         };
32482         
32483         return cfg;
32484         
32485     },
32486     
32487     initEvents: function() 
32488     {
32489         
32490     },
32491     
32492     onRender : function(ct, position) 
32493     {
32494         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32495         
32496         if(this.bullets.length){
32497             Roo.each(this.bullets, function(b){
32498                this.addItem(b);
32499             }, this);
32500         }
32501         
32502         this.format();
32503         
32504     },
32505     
32506     addItem : function(cfg)
32507     {
32508         var item = new Roo.bootstrap.NavProgressItem(cfg);
32509         
32510         item.parentId = this.id;
32511         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32512         
32513         if(cfg.html){
32514             var top = new Roo.bootstrap.Element({
32515                 tag : 'div',
32516                 cls : 'roo-navigation-bar-text'
32517             });
32518             
32519             var bottom = new Roo.bootstrap.Element({
32520                 tag : 'div',
32521                 cls : 'roo-navigation-bar-text'
32522             });
32523             
32524             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32525             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32526             
32527             var topText = new Roo.bootstrap.Element({
32528                 tag : 'span',
32529                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32530             });
32531             
32532             var bottomText = new Roo.bootstrap.Element({
32533                 tag : 'span',
32534                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32535             });
32536             
32537             topText.onRender(top.el, null);
32538             bottomText.onRender(bottom.el, null);
32539             
32540             item.topEl = top;
32541             item.bottomEl = bottom;
32542         }
32543         
32544         this.barItems.push(item);
32545         
32546         return item;
32547     },
32548     
32549     getActive : function()
32550     {
32551         var active = false;
32552         
32553         Roo.each(this.barItems, function(v){
32554             
32555             if (!v.isActive()) {
32556                 return;
32557             }
32558             
32559             active = v;
32560             return false;
32561             
32562         });
32563         
32564         return active;
32565     },
32566     
32567     setActiveItem : function(item)
32568     {
32569         var prev = false;
32570         
32571         Roo.each(this.barItems, function(v){
32572             if (v.rid == item.rid) {
32573                 return ;
32574             }
32575             
32576             if (v.isActive()) {
32577                 v.setActive(false);
32578                 prev = v;
32579             }
32580         });
32581
32582         item.setActive(true);
32583         
32584         this.fireEvent('changed', this, item, prev);
32585     },
32586     
32587     getBarItem: function(rid)
32588     {
32589         var ret = false;
32590         
32591         Roo.each(this.barItems, function(e) {
32592             if (e.rid != rid) {
32593                 return;
32594             }
32595             
32596             ret =  e;
32597             return false;
32598         });
32599         
32600         return ret;
32601     },
32602     
32603     indexOfItem : function(item)
32604     {
32605         var index = false;
32606         
32607         Roo.each(this.barItems, function(v, i){
32608             
32609             if (v.rid != item.rid) {
32610                 return;
32611             }
32612             
32613             index = i;
32614             return false
32615         });
32616         
32617         return index;
32618     },
32619     
32620     setActiveNext : function()
32621     {
32622         var i = this.indexOfItem(this.getActive());
32623         
32624         if (i > this.barItems.length) {
32625             return;
32626         }
32627         
32628         this.setActiveItem(this.barItems[i+1]);
32629     },
32630     
32631     setActivePrev : function()
32632     {
32633         var i = this.indexOfItem(this.getActive());
32634         
32635         if (i  < 1) {
32636             return;
32637         }
32638         
32639         this.setActiveItem(this.barItems[i-1]);
32640     },
32641     
32642     format : function()
32643     {
32644         if(!this.barItems.length){
32645             return;
32646         }
32647      
32648         var width = 100 / this.barItems.length;
32649         
32650         Roo.each(this.barItems, function(i){
32651             i.el.setStyle('width', width + '%');
32652             i.topEl.el.setStyle('width', width + '%');
32653             i.bottomEl.el.setStyle('width', width + '%');
32654         }, this);
32655         
32656     }
32657     
32658 });
32659 /*
32660  * - LGPL
32661  *
32662  * Nav Progress Item
32663  * 
32664  */
32665
32666 /**
32667  * @class Roo.bootstrap.NavProgressItem
32668  * @extends Roo.bootstrap.Component
32669  * Bootstrap NavProgressItem class
32670  * @cfg {String} rid the reference id
32671  * @cfg {Boolean} active (true|false) Is item active default false
32672  * @cfg {Boolean} disabled (true|false) Is item active default false
32673  * @cfg {String} html
32674  * @cfg {String} position (top|bottom) text position default bottom
32675  * @cfg {String} icon show icon instead of number
32676  * 
32677  * @constructor
32678  * Create a new NavProgressItem
32679  * @param {Object} config The config object
32680  */
32681 Roo.bootstrap.NavProgressItem = function(config){
32682     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32683     this.addEvents({
32684         // raw events
32685         /**
32686          * @event click
32687          * The raw click event for the entire grid.
32688          * @param {Roo.bootstrap.NavProgressItem} this
32689          * @param {Roo.EventObject} e
32690          */
32691         "click" : true
32692     });
32693    
32694 };
32695
32696 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32697     
32698     rid : '',
32699     active : false,
32700     disabled : false,
32701     html : '',
32702     position : 'bottom',
32703     icon : false,
32704     
32705     getAutoCreate : function()
32706     {
32707         var iconCls = 'roo-navigation-bar-item-icon';
32708         
32709         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32710         
32711         var cfg = {
32712             tag: 'li',
32713             cls: 'roo-navigation-bar-item',
32714             cn : [
32715                 {
32716                     tag : 'i',
32717                     cls : iconCls
32718                 }
32719             ]
32720         };
32721         
32722         if(this.active){
32723             cfg.cls += ' active';
32724         }
32725         if(this.disabled){
32726             cfg.cls += ' disabled';
32727         }
32728         
32729         return cfg;
32730     },
32731     
32732     disable : function()
32733     {
32734         this.setDisabled(true);
32735     },
32736     
32737     enable : function()
32738     {
32739         this.setDisabled(false);
32740     },
32741     
32742     initEvents: function() 
32743     {
32744         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32745         
32746         this.iconEl.on('click', this.onClick, this);
32747     },
32748     
32749     onClick : function(e)
32750     {
32751         e.preventDefault();
32752         
32753         if(this.disabled){
32754             return;
32755         }
32756         
32757         if(this.fireEvent('click', this, e) === false){
32758             return;
32759         };
32760         
32761         this.parent().setActiveItem(this);
32762     },
32763     
32764     isActive: function () 
32765     {
32766         return this.active;
32767     },
32768     
32769     setActive : function(state)
32770     {
32771         if(this.active == state){
32772             return;
32773         }
32774         
32775         this.active = state;
32776         
32777         if (state) {
32778             this.el.addClass('active');
32779             return;
32780         }
32781         
32782         this.el.removeClass('active');
32783         
32784         return;
32785     },
32786     
32787     setDisabled : function(state)
32788     {
32789         if(this.disabled == state){
32790             return;
32791         }
32792         
32793         this.disabled = state;
32794         
32795         if (state) {
32796             this.el.addClass('disabled');
32797             return;
32798         }
32799         
32800         this.el.removeClass('disabled');
32801     },
32802     
32803     tooltipEl : function()
32804     {
32805         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32806     }
32807 });
32808  
32809
32810  /*
32811  * - LGPL
32812  *
32813  * FieldLabel
32814  * 
32815  */
32816
32817 /**
32818  * @class Roo.bootstrap.FieldLabel
32819  * @extends Roo.bootstrap.Component
32820  * Bootstrap FieldLabel class
32821  * @cfg {String} html contents of the element
32822  * @cfg {String} tag tag of the element default label
32823  * @cfg {String} cls class of the element
32824  * @cfg {String} target label target 
32825  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32826  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32827  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32828  * @cfg {String} iconTooltip default "This field is required"
32829  * @cfg {String} indicatorpos (left|right) default left
32830  * 
32831  * @constructor
32832  * Create a new FieldLabel
32833  * @param {Object} config The config object
32834  */
32835
32836 Roo.bootstrap.FieldLabel = function(config){
32837     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32838     
32839     this.addEvents({
32840             /**
32841              * @event invalid
32842              * Fires after the field has been marked as invalid.
32843              * @param {Roo.form.FieldLabel} this
32844              * @param {String} msg The validation message
32845              */
32846             invalid : true,
32847             /**
32848              * @event valid
32849              * Fires after the field has been validated with no errors.
32850              * @param {Roo.form.FieldLabel} this
32851              */
32852             valid : true
32853         });
32854 };
32855
32856 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32857     
32858     tag: 'label',
32859     cls: '',
32860     html: '',
32861     target: '',
32862     allowBlank : true,
32863     invalidClass : 'has-warning',
32864     validClass : 'has-success',
32865     iconTooltip : 'This field is required',
32866     indicatorpos : 'left',
32867     
32868     getAutoCreate : function(){
32869         
32870         var cls = "";
32871         if (!this.allowBlank) {
32872             cls  = "visible";
32873         }
32874         
32875         var cfg = {
32876             tag : this.tag,
32877             cls : 'roo-bootstrap-field-label ' + this.cls,
32878             for : this.target,
32879             cn : [
32880                 {
32881                     tag : 'i',
32882                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32883                     tooltip : this.iconTooltip
32884                 },
32885                 {
32886                     tag : 'span',
32887                     html : this.html
32888                 }
32889             ] 
32890         };
32891         
32892         if(this.indicatorpos == 'right'){
32893             var cfg = {
32894                 tag : this.tag,
32895                 cls : 'roo-bootstrap-field-label ' + this.cls,
32896                 for : this.target,
32897                 cn : [
32898                     {
32899                         tag : 'span',
32900                         html : this.html
32901                     },
32902                     {
32903                         tag : 'i',
32904                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32905                         tooltip : this.iconTooltip
32906                     }
32907                 ] 
32908             };
32909         }
32910         
32911         return cfg;
32912     },
32913     
32914     initEvents: function() 
32915     {
32916         Roo.bootstrap.Element.superclass.initEvents.call(this);
32917         
32918         this.indicator = this.indicatorEl();
32919         
32920         if(this.indicator){
32921             this.indicator.removeClass('visible');
32922             this.indicator.addClass('invisible');
32923         }
32924         
32925         Roo.bootstrap.FieldLabel.register(this);
32926     },
32927     
32928     indicatorEl : function()
32929     {
32930         var indicator = this.el.select('i.roo-required-indicator',true).first();
32931         
32932         if(!indicator){
32933             return false;
32934         }
32935         
32936         return indicator;
32937         
32938     },
32939     
32940     /**
32941      * Mark this field as valid
32942      */
32943     markValid : function()
32944     {
32945         if(this.indicator){
32946             this.indicator.removeClass('visible');
32947             this.indicator.addClass('invisible');
32948         }
32949         if (Roo.bootstrap.version == 3) {
32950             this.el.removeClass(this.invalidClass);
32951             this.el.addClass(this.validClass);
32952         } else {
32953             this.el.removeClass('is-invalid');
32954             this.el.addClass('is-valid');
32955         }
32956         
32957         
32958         this.fireEvent('valid', this);
32959     },
32960     
32961     /**
32962      * Mark this field as invalid
32963      * @param {String} msg The validation message
32964      */
32965     markInvalid : function(msg)
32966     {
32967         if(this.indicator){
32968             this.indicator.removeClass('invisible');
32969             this.indicator.addClass('visible');
32970         }
32971           if (Roo.bootstrap.version == 3) {
32972             this.el.removeClass(this.validClass);
32973             this.el.addClass(this.invalidClass);
32974         } else {
32975             this.el.removeClass('is-valid');
32976             this.el.addClass('is-invalid');
32977         }
32978         
32979         
32980         this.fireEvent('invalid', this, msg);
32981     }
32982     
32983    
32984 });
32985
32986 Roo.apply(Roo.bootstrap.FieldLabel, {
32987     
32988     groups: {},
32989     
32990      /**
32991     * register a FieldLabel Group
32992     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32993     */
32994     register : function(label)
32995     {
32996         if(this.groups.hasOwnProperty(label.target)){
32997             return;
32998         }
32999      
33000         this.groups[label.target] = label;
33001         
33002     },
33003     /**
33004     * fetch a FieldLabel Group based on the target
33005     * @param {string} target
33006     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33007     */
33008     get: function(target) {
33009         if (typeof(this.groups[target]) == 'undefined') {
33010             return false;
33011         }
33012         
33013         return this.groups[target] ;
33014     }
33015 });
33016
33017  
33018
33019  /*
33020  * - LGPL
33021  *
33022  * page DateSplitField.
33023  * 
33024  */
33025
33026
33027 /**
33028  * @class Roo.bootstrap.DateSplitField
33029  * @extends Roo.bootstrap.Component
33030  * Bootstrap DateSplitField class
33031  * @cfg {string} fieldLabel - the label associated
33032  * @cfg {Number} labelWidth set the width of label (0-12)
33033  * @cfg {String} labelAlign (top|left)
33034  * @cfg {Boolean} dayAllowBlank (true|false) default false
33035  * @cfg {Boolean} monthAllowBlank (true|false) default false
33036  * @cfg {Boolean} yearAllowBlank (true|false) default false
33037  * @cfg {string} dayPlaceholder 
33038  * @cfg {string} monthPlaceholder
33039  * @cfg {string} yearPlaceholder
33040  * @cfg {string} dayFormat default 'd'
33041  * @cfg {string} monthFormat default 'm'
33042  * @cfg {string} yearFormat default 'Y'
33043  * @cfg {Number} labellg set the width of label (1-12)
33044  * @cfg {Number} labelmd set the width of label (1-12)
33045  * @cfg {Number} labelsm set the width of label (1-12)
33046  * @cfg {Number} labelxs set the width of label (1-12)
33047
33048  *     
33049  * @constructor
33050  * Create a new DateSplitField
33051  * @param {Object} config The config object
33052  */
33053
33054 Roo.bootstrap.DateSplitField = function(config){
33055     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33056     
33057     this.addEvents({
33058         // raw events
33059          /**
33060          * @event years
33061          * getting the data of years
33062          * @param {Roo.bootstrap.DateSplitField} this
33063          * @param {Object} years
33064          */
33065         "years" : true,
33066         /**
33067          * @event days
33068          * getting the data of days
33069          * @param {Roo.bootstrap.DateSplitField} this
33070          * @param {Object} days
33071          */
33072         "days" : true,
33073         /**
33074          * @event invalid
33075          * Fires after the field has been marked as invalid.
33076          * @param {Roo.form.Field} this
33077          * @param {String} msg The validation message
33078          */
33079         invalid : true,
33080        /**
33081          * @event valid
33082          * Fires after the field has been validated with no errors.
33083          * @param {Roo.form.Field} this
33084          */
33085         valid : true
33086     });
33087 };
33088
33089 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33090     
33091     fieldLabel : '',
33092     labelAlign : 'top',
33093     labelWidth : 3,
33094     dayAllowBlank : false,
33095     monthAllowBlank : false,
33096     yearAllowBlank : false,
33097     dayPlaceholder : '',
33098     monthPlaceholder : '',
33099     yearPlaceholder : '',
33100     dayFormat : 'd',
33101     monthFormat : 'm',
33102     yearFormat : 'Y',
33103     isFormField : true,
33104     labellg : 0,
33105     labelmd : 0,
33106     labelsm : 0,
33107     labelxs : 0,
33108     
33109     getAutoCreate : function()
33110     {
33111         var cfg = {
33112             tag : 'div',
33113             cls : 'row roo-date-split-field-group',
33114             cn : [
33115                 {
33116                     tag : 'input',
33117                     type : 'hidden',
33118                     cls : 'form-hidden-field roo-date-split-field-group-value',
33119                     name : this.name
33120                 }
33121             ]
33122         };
33123         
33124         var labelCls = 'col-md-12';
33125         var contentCls = 'col-md-4';
33126         
33127         if(this.fieldLabel){
33128             
33129             var label = {
33130                 tag : 'div',
33131                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33132                 cn : [
33133                     {
33134                         tag : 'label',
33135                         html : this.fieldLabel
33136                     }
33137                 ]
33138             };
33139             
33140             if(this.labelAlign == 'left'){
33141             
33142                 if(this.labelWidth > 12){
33143                     label.style = "width: " + this.labelWidth + 'px';
33144                 }
33145
33146                 if(this.labelWidth < 13 && this.labelmd == 0){
33147                     this.labelmd = this.labelWidth;
33148                 }
33149
33150                 if(this.labellg > 0){
33151                     labelCls = ' col-lg-' + this.labellg;
33152                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33153                 }
33154
33155                 if(this.labelmd > 0){
33156                     labelCls = ' col-md-' + this.labelmd;
33157                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33158                 }
33159
33160                 if(this.labelsm > 0){
33161                     labelCls = ' col-sm-' + this.labelsm;
33162                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33163                 }
33164
33165                 if(this.labelxs > 0){
33166                     labelCls = ' col-xs-' + this.labelxs;
33167                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33168                 }
33169             }
33170             
33171             label.cls += ' ' + labelCls;
33172             
33173             cfg.cn.push(label);
33174         }
33175         
33176         Roo.each(['day', 'month', 'year'], function(t){
33177             cfg.cn.push({
33178                 tag : 'div',
33179                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33180             });
33181         }, this);
33182         
33183         return cfg;
33184     },
33185     
33186     inputEl: function ()
33187     {
33188         return this.el.select('.roo-date-split-field-group-value', true).first();
33189     },
33190     
33191     onRender : function(ct, position) 
33192     {
33193         var _this = this;
33194         
33195         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33196         
33197         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33198         
33199         this.dayField = new Roo.bootstrap.ComboBox({
33200             allowBlank : this.dayAllowBlank,
33201             alwaysQuery : true,
33202             displayField : 'value',
33203             editable : false,
33204             fieldLabel : '',
33205             forceSelection : true,
33206             mode : 'local',
33207             placeholder : this.dayPlaceholder,
33208             selectOnFocus : true,
33209             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33210             triggerAction : 'all',
33211             typeAhead : true,
33212             valueField : 'value',
33213             store : new Roo.data.SimpleStore({
33214                 data : (function() {    
33215                     var days = [];
33216                     _this.fireEvent('days', _this, days);
33217                     return days;
33218                 })(),
33219                 fields : [ 'value' ]
33220             }),
33221             listeners : {
33222                 select : function (_self, record, index)
33223                 {
33224                     _this.setValue(_this.getValue());
33225                 }
33226             }
33227         });
33228
33229         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33230         
33231         this.monthField = new Roo.bootstrap.MonthField({
33232             after : '<i class=\"fa fa-calendar\"></i>',
33233             allowBlank : this.monthAllowBlank,
33234             placeholder : this.monthPlaceholder,
33235             readOnly : true,
33236             listeners : {
33237                 render : function (_self)
33238                 {
33239                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33240                         e.preventDefault();
33241                         _self.focus();
33242                     });
33243                 },
33244                 select : function (_self, oldvalue, newvalue)
33245                 {
33246                     _this.setValue(_this.getValue());
33247                 }
33248             }
33249         });
33250         
33251         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33252         
33253         this.yearField = new Roo.bootstrap.ComboBox({
33254             allowBlank : this.yearAllowBlank,
33255             alwaysQuery : true,
33256             displayField : 'value',
33257             editable : false,
33258             fieldLabel : '',
33259             forceSelection : true,
33260             mode : 'local',
33261             placeholder : this.yearPlaceholder,
33262             selectOnFocus : true,
33263             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33264             triggerAction : 'all',
33265             typeAhead : true,
33266             valueField : 'value',
33267             store : new Roo.data.SimpleStore({
33268                 data : (function() {
33269                     var years = [];
33270                     _this.fireEvent('years', _this, years);
33271                     return years;
33272                 })(),
33273                 fields : [ 'value' ]
33274             }),
33275             listeners : {
33276                 select : function (_self, record, index)
33277                 {
33278                     _this.setValue(_this.getValue());
33279                 }
33280             }
33281         });
33282
33283         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33284     },
33285     
33286     setValue : function(v, format)
33287     {
33288         this.inputEl.dom.value = v;
33289         
33290         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33291         
33292         var d = Date.parseDate(v, f);
33293         
33294         if(!d){
33295             this.validate();
33296             return;
33297         }
33298         
33299         this.setDay(d.format(this.dayFormat));
33300         this.setMonth(d.format(this.monthFormat));
33301         this.setYear(d.format(this.yearFormat));
33302         
33303         this.validate();
33304         
33305         return;
33306     },
33307     
33308     setDay : function(v)
33309     {
33310         this.dayField.setValue(v);
33311         this.inputEl.dom.value = this.getValue();
33312         this.validate();
33313         return;
33314     },
33315     
33316     setMonth : function(v)
33317     {
33318         this.monthField.setValue(v, true);
33319         this.inputEl.dom.value = this.getValue();
33320         this.validate();
33321         return;
33322     },
33323     
33324     setYear : function(v)
33325     {
33326         this.yearField.setValue(v);
33327         this.inputEl.dom.value = this.getValue();
33328         this.validate();
33329         return;
33330     },
33331     
33332     getDay : function()
33333     {
33334         return this.dayField.getValue();
33335     },
33336     
33337     getMonth : function()
33338     {
33339         return this.monthField.getValue();
33340     },
33341     
33342     getYear : function()
33343     {
33344         return this.yearField.getValue();
33345     },
33346     
33347     getValue : function()
33348     {
33349         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33350         
33351         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33352         
33353         return date;
33354     },
33355     
33356     reset : function()
33357     {
33358         this.setDay('');
33359         this.setMonth('');
33360         this.setYear('');
33361         this.inputEl.dom.value = '';
33362         this.validate();
33363         return;
33364     },
33365     
33366     validate : function()
33367     {
33368         var d = this.dayField.validate();
33369         var m = this.monthField.validate();
33370         var y = this.yearField.validate();
33371         
33372         var valid = true;
33373         
33374         if(
33375                 (!this.dayAllowBlank && !d) ||
33376                 (!this.monthAllowBlank && !m) ||
33377                 (!this.yearAllowBlank && !y)
33378         ){
33379             valid = false;
33380         }
33381         
33382         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33383             return valid;
33384         }
33385         
33386         if(valid){
33387             this.markValid();
33388             return valid;
33389         }
33390         
33391         this.markInvalid();
33392         
33393         return valid;
33394     },
33395     
33396     markValid : function()
33397     {
33398         
33399         var label = this.el.select('label', true).first();
33400         var icon = this.el.select('i.fa-star', true).first();
33401
33402         if(label && icon){
33403             icon.remove();
33404         }
33405         
33406         this.fireEvent('valid', this);
33407     },
33408     
33409      /**
33410      * Mark this field as invalid
33411      * @param {String} msg The validation message
33412      */
33413     markInvalid : function(msg)
33414     {
33415         
33416         var label = this.el.select('label', true).first();
33417         var icon = this.el.select('i.fa-star', true).first();
33418
33419         if(label && !icon){
33420             this.el.select('.roo-date-split-field-label', true).createChild({
33421                 tag : 'i',
33422                 cls : 'text-danger fa fa-lg fa-star',
33423                 tooltip : 'This field is required',
33424                 style : 'margin-right:5px;'
33425             }, label, true);
33426         }
33427         
33428         this.fireEvent('invalid', this, msg);
33429     },
33430     
33431     clearInvalid : function()
33432     {
33433         var label = this.el.select('label', true).first();
33434         var icon = this.el.select('i.fa-star', true).first();
33435
33436         if(label && icon){
33437             icon.remove();
33438         }
33439         
33440         this.fireEvent('valid', this);
33441     },
33442     
33443     getName: function()
33444     {
33445         return this.name;
33446     }
33447     
33448 });
33449
33450  /**
33451  *
33452  * This is based on 
33453  * http://masonry.desandro.com
33454  *
33455  * The idea is to render all the bricks based on vertical width...
33456  *
33457  * The original code extends 'outlayer' - we might need to use that....
33458  * 
33459  */
33460
33461
33462 /**
33463  * @class Roo.bootstrap.LayoutMasonry
33464  * @extends Roo.bootstrap.Component
33465  * Bootstrap Layout Masonry class
33466  * 
33467  * @constructor
33468  * Create a new Element
33469  * @param {Object} config The config object
33470  */
33471
33472 Roo.bootstrap.LayoutMasonry = function(config){
33473     
33474     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33475     
33476     this.bricks = [];
33477     
33478     Roo.bootstrap.LayoutMasonry.register(this);
33479     
33480     this.addEvents({
33481         // raw events
33482         /**
33483          * @event layout
33484          * Fire after layout the items
33485          * @param {Roo.bootstrap.LayoutMasonry} this
33486          * @param {Roo.EventObject} e
33487          */
33488         "layout" : true
33489     });
33490     
33491 };
33492
33493 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33494     
33495     /**
33496      * @cfg {Boolean} isLayoutInstant = no animation?
33497      */   
33498     isLayoutInstant : false, // needed?
33499    
33500     /**
33501      * @cfg {Number} boxWidth  width of the columns
33502      */   
33503     boxWidth : 450,
33504     
33505       /**
33506      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33507      */   
33508     boxHeight : 0,
33509     
33510     /**
33511      * @cfg {Number} padWidth padding below box..
33512      */   
33513     padWidth : 10, 
33514     
33515     /**
33516      * @cfg {Number} gutter gutter width..
33517      */   
33518     gutter : 10,
33519     
33520      /**
33521      * @cfg {Number} maxCols maximum number of columns
33522      */   
33523     
33524     maxCols: 0,
33525     
33526     /**
33527      * @cfg {Boolean} isAutoInitial defalut true
33528      */   
33529     isAutoInitial : true, 
33530     
33531     containerWidth: 0,
33532     
33533     /**
33534      * @cfg {Boolean} isHorizontal defalut false
33535      */   
33536     isHorizontal : false, 
33537
33538     currentSize : null,
33539     
33540     tag: 'div',
33541     
33542     cls: '',
33543     
33544     bricks: null, //CompositeElement
33545     
33546     cols : 1,
33547     
33548     _isLayoutInited : false,
33549     
33550 //    isAlternative : false, // only use for vertical layout...
33551     
33552     /**
33553      * @cfg {Number} alternativePadWidth padding below box..
33554      */   
33555     alternativePadWidth : 50,
33556     
33557     selectedBrick : [],
33558     
33559     getAutoCreate : function(){
33560         
33561         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33562         
33563         var cfg = {
33564             tag: this.tag,
33565             cls: 'blog-masonary-wrapper ' + this.cls,
33566             cn : {
33567                 cls : 'mas-boxes masonary'
33568             }
33569         };
33570         
33571         return cfg;
33572     },
33573     
33574     getChildContainer: function( )
33575     {
33576         if (this.boxesEl) {
33577             return this.boxesEl;
33578         }
33579         
33580         this.boxesEl = this.el.select('.mas-boxes').first();
33581         
33582         return this.boxesEl;
33583     },
33584     
33585     
33586     initEvents : function()
33587     {
33588         var _this = this;
33589         
33590         if(this.isAutoInitial){
33591             Roo.log('hook children rendered');
33592             this.on('childrenrendered', function() {
33593                 Roo.log('children rendered');
33594                 _this.initial();
33595             } ,this);
33596         }
33597     },
33598     
33599     initial : function()
33600     {
33601         this.selectedBrick = [];
33602         
33603         this.currentSize = this.el.getBox(true);
33604         
33605         Roo.EventManager.onWindowResize(this.resize, this); 
33606
33607         if(!this.isAutoInitial){
33608             this.layout();
33609             return;
33610         }
33611         
33612         this.layout();
33613         
33614         return;
33615         //this.layout.defer(500,this);
33616         
33617     },
33618     
33619     resize : function()
33620     {
33621         var cs = this.el.getBox(true);
33622         
33623         if (
33624                 this.currentSize.width == cs.width && 
33625                 this.currentSize.x == cs.x && 
33626                 this.currentSize.height == cs.height && 
33627                 this.currentSize.y == cs.y 
33628         ) {
33629             Roo.log("no change in with or X or Y");
33630             return;
33631         }
33632         
33633         this.currentSize = cs;
33634         
33635         this.layout();
33636         
33637     },
33638     
33639     layout : function()
33640     {   
33641         this._resetLayout();
33642         
33643         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33644         
33645         this.layoutItems( isInstant );
33646       
33647         this._isLayoutInited = true;
33648         
33649         this.fireEvent('layout', this);
33650         
33651     },
33652     
33653     _resetLayout : function()
33654     {
33655         if(this.isHorizontal){
33656             this.horizontalMeasureColumns();
33657             return;
33658         }
33659         
33660         this.verticalMeasureColumns();
33661         
33662     },
33663     
33664     verticalMeasureColumns : function()
33665     {
33666         this.getContainerWidth();
33667         
33668 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33669 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33670 //            return;
33671 //        }
33672         
33673         var boxWidth = this.boxWidth + this.padWidth;
33674         
33675         if(this.containerWidth < this.boxWidth){
33676             boxWidth = this.containerWidth
33677         }
33678         
33679         var containerWidth = this.containerWidth;
33680         
33681         var cols = Math.floor(containerWidth / boxWidth);
33682         
33683         this.cols = Math.max( cols, 1 );
33684         
33685         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33686         
33687         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33688         
33689         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33690         
33691         this.colWidth = boxWidth + avail - this.padWidth;
33692         
33693         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33694         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33695     },
33696     
33697     horizontalMeasureColumns : function()
33698     {
33699         this.getContainerWidth();
33700         
33701         var boxWidth = this.boxWidth;
33702         
33703         if(this.containerWidth < boxWidth){
33704             boxWidth = this.containerWidth;
33705         }
33706         
33707         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33708         
33709         this.el.setHeight(boxWidth);
33710         
33711     },
33712     
33713     getContainerWidth : function()
33714     {
33715         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33716     },
33717     
33718     layoutItems : function( isInstant )
33719     {
33720         Roo.log(this.bricks);
33721         
33722         var items = Roo.apply([], this.bricks);
33723         
33724         if(this.isHorizontal){
33725             this._horizontalLayoutItems( items , isInstant );
33726             return;
33727         }
33728         
33729 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33730 //            this._verticalAlternativeLayoutItems( items , isInstant );
33731 //            return;
33732 //        }
33733         
33734         this._verticalLayoutItems( items , isInstant );
33735         
33736     },
33737     
33738     _verticalLayoutItems : function ( items , isInstant)
33739     {
33740         if ( !items || !items.length ) {
33741             return;
33742         }
33743         
33744         var standard = [
33745             ['xs', 'xs', 'xs', 'tall'],
33746             ['xs', 'xs', 'tall'],
33747             ['xs', 'xs', 'sm'],
33748             ['xs', 'xs', 'xs'],
33749             ['xs', 'tall'],
33750             ['xs', 'sm'],
33751             ['xs', 'xs'],
33752             ['xs'],
33753             
33754             ['sm', 'xs', 'xs'],
33755             ['sm', 'xs'],
33756             ['sm'],
33757             
33758             ['tall', 'xs', 'xs', 'xs'],
33759             ['tall', 'xs', 'xs'],
33760             ['tall', 'xs'],
33761             ['tall']
33762             
33763         ];
33764         
33765         var queue = [];
33766         
33767         var boxes = [];
33768         
33769         var box = [];
33770         
33771         Roo.each(items, function(item, k){
33772             
33773             switch (item.size) {
33774                 // these layouts take up a full box,
33775                 case 'md' :
33776                 case 'md-left' :
33777                 case 'md-right' :
33778                 case 'wide' :
33779                     
33780                     if(box.length){
33781                         boxes.push(box);
33782                         box = [];
33783                     }
33784                     
33785                     boxes.push([item]);
33786                     
33787                     break;
33788                     
33789                 case 'xs' :
33790                 case 'sm' :
33791                 case 'tall' :
33792                     
33793                     box.push(item);
33794                     
33795                     break;
33796                 default :
33797                     break;
33798                     
33799             }
33800             
33801         }, this);
33802         
33803         if(box.length){
33804             boxes.push(box);
33805             box = [];
33806         }
33807         
33808         var filterPattern = function(box, length)
33809         {
33810             if(!box.length){
33811                 return;
33812             }
33813             
33814             var match = false;
33815             
33816             var pattern = box.slice(0, length);
33817             
33818             var format = [];
33819             
33820             Roo.each(pattern, function(i){
33821                 format.push(i.size);
33822             }, this);
33823             
33824             Roo.each(standard, function(s){
33825                 
33826                 if(String(s) != String(format)){
33827                     return;
33828                 }
33829                 
33830                 match = true;
33831                 return false;
33832                 
33833             }, this);
33834             
33835             if(!match && length == 1){
33836                 return;
33837             }
33838             
33839             if(!match){
33840                 filterPattern(box, length - 1);
33841                 return;
33842             }
33843                 
33844             queue.push(pattern);
33845
33846             box = box.slice(length, box.length);
33847
33848             filterPattern(box, 4);
33849
33850             return;
33851             
33852         }
33853         
33854         Roo.each(boxes, function(box, k){
33855             
33856             if(!box.length){
33857                 return;
33858             }
33859             
33860             if(box.length == 1){
33861                 queue.push(box);
33862                 return;
33863             }
33864             
33865             filterPattern(box, 4);
33866             
33867         }, this);
33868         
33869         this._processVerticalLayoutQueue( queue, isInstant );
33870         
33871     },
33872     
33873 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33874 //    {
33875 //        if ( !items || !items.length ) {
33876 //            return;
33877 //        }
33878 //
33879 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33880 //        
33881 //    },
33882     
33883     _horizontalLayoutItems : function ( items , isInstant)
33884     {
33885         if ( !items || !items.length || items.length < 3) {
33886             return;
33887         }
33888         
33889         items.reverse();
33890         
33891         var eItems = items.slice(0, 3);
33892         
33893         items = items.slice(3, items.length);
33894         
33895         var standard = [
33896             ['xs', 'xs', 'xs', 'wide'],
33897             ['xs', 'xs', 'wide'],
33898             ['xs', 'xs', 'sm'],
33899             ['xs', 'xs', 'xs'],
33900             ['xs', 'wide'],
33901             ['xs', 'sm'],
33902             ['xs', 'xs'],
33903             ['xs'],
33904             
33905             ['sm', 'xs', 'xs'],
33906             ['sm', 'xs'],
33907             ['sm'],
33908             
33909             ['wide', 'xs', 'xs', 'xs'],
33910             ['wide', 'xs', 'xs'],
33911             ['wide', 'xs'],
33912             ['wide'],
33913             
33914             ['wide-thin']
33915         ];
33916         
33917         var queue = [];
33918         
33919         var boxes = [];
33920         
33921         var box = [];
33922         
33923         Roo.each(items, function(item, k){
33924             
33925             switch (item.size) {
33926                 case 'md' :
33927                 case 'md-left' :
33928                 case 'md-right' :
33929                 case 'tall' :
33930                     
33931                     if(box.length){
33932                         boxes.push(box);
33933                         box = [];
33934                     }
33935                     
33936                     boxes.push([item]);
33937                     
33938                     break;
33939                     
33940                 case 'xs' :
33941                 case 'sm' :
33942                 case 'wide' :
33943                 case 'wide-thin' :
33944                     
33945                     box.push(item);
33946                     
33947                     break;
33948                 default :
33949                     break;
33950                     
33951             }
33952             
33953         }, this);
33954         
33955         if(box.length){
33956             boxes.push(box);
33957             box = [];
33958         }
33959         
33960         var filterPattern = function(box, length)
33961         {
33962             if(!box.length){
33963                 return;
33964             }
33965             
33966             var match = false;
33967             
33968             var pattern = box.slice(0, length);
33969             
33970             var format = [];
33971             
33972             Roo.each(pattern, function(i){
33973                 format.push(i.size);
33974             }, this);
33975             
33976             Roo.each(standard, function(s){
33977                 
33978                 if(String(s) != String(format)){
33979                     return;
33980                 }
33981                 
33982                 match = true;
33983                 return false;
33984                 
33985             }, this);
33986             
33987             if(!match && length == 1){
33988                 return;
33989             }
33990             
33991             if(!match){
33992                 filterPattern(box, length - 1);
33993                 return;
33994             }
33995                 
33996             queue.push(pattern);
33997
33998             box = box.slice(length, box.length);
33999
34000             filterPattern(box, 4);
34001
34002             return;
34003             
34004         }
34005         
34006         Roo.each(boxes, function(box, k){
34007             
34008             if(!box.length){
34009                 return;
34010             }
34011             
34012             if(box.length == 1){
34013                 queue.push(box);
34014                 return;
34015             }
34016             
34017             filterPattern(box, 4);
34018             
34019         }, this);
34020         
34021         
34022         var prune = [];
34023         
34024         var pos = this.el.getBox(true);
34025         
34026         var minX = pos.x;
34027         
34028         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34029         
34030         var hit_end = false;
34031         
34032         Roo.each(queue, function(box){
34033             
34034             if(hit_end){
34035                 
34036                 Roo.each(box, function(b){
34037                 
34038                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34039                     b.el.hide();
34040
34041                 }, this);
34042
34043                 return;
34044             }
34045             
34046             var mx = 0;
34047             
34048             Roo.each(box, function(b){
34049                 
34050                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34051                 b.el.show();
34052
34053                 mx = Math.max(mx, b.x);
34054                 
34055             }, this);
34056             
34057             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34058             
34059             if(maxX < minX){
34060                 
34061                 Roo.each(box, function(b){
34062                 
34063                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34064                     b.el.hide();
34065                     
34066                 }, this);
34067                 
34068                 hit_end = true;
34069                 
34070                 return;
34071             }
34072             
34073             prune.push(box);
34074             
34075         }, this);
34076         
34077         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34078     },
34079     
34080     /** Sets position of item in DOM
34081     * @param {Element} item
34082     * @param {Number} x - horizontal position
34083     * @param {Number} y - vertical position
34084     * @param {Boolean} isInstant - disables transitions
34085     */
34086     _processVerticalLayoutQueue : function( queue, isInstant )
34087     {
34088         var pos = this.el.getBox(true);
34089         var x = pos.x;
34090         var y = pos.y;
34091         var maxY = [];
34092         
34093         for (var i = 0; i < this.cols; i++){
34094             maxY[i] = pos.y;
34095         }
34096         
34097         Roo.each(queue, function(box, k){
34098             
34099             var col = k % this.cols;
34100             
34101             Roo.each(box, function(b,kk){
34102                 
34103                 b.el.position('absolute');
34104                 
34105                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34106                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34107                 
34108                 if(b.size == 'md-left' || b.size == 'md-right'){
34109                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34110                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34111                 }
34112                 
34113                 b.el.setWidth(width);
34114                 b.el.setHeight(height);
34115                 // iframe?
34116                 b.el.select('iframe',true).setSize(width,height);
34117                 
34118             }, this);
34119             
34120             for (var i = 0; i < this.cols; i++){
34121                 
34122                 if(maxY[i] < maxY[col]){
34123                     col = i;
34124                     continue;
34125                 }
34126                 
34127                 col = Math.min(col, i);
34128                 
34129             }
34130             
34131             x = pos.x + col * (this.colWidth + this.padWidth);
34132             
34133             y = maxY[col];
34134             
34135             var positions = [];
34136             
34137             switch (box.length){
34138                 case 1 :
34139                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34140                     break;
34141                 case 2 :
34142                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34143                     break;
34144                 case 3 :
34145                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34146                     break;
34147                 case 4 :
34148                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34149                     break;
34150                 default :
34151                     break;
34152             }
34153             
34154             Roo.each(box, function(b,kk){
34155                 
34156                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34157                 
34158                 var sz = b.el.getSize();
34159                 
34160                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34161                 
34162             }, this);
34163             
34164         }, this);
34165         
34166         var mY = 0;
34167         
34168         for (var i = 0; i < this.cols; i++){
34169             mY = Math.max(mY, maxY[i]);
34170         }
34171         
34172         this.el.setHeight(mY - pos.y);
34173         
34174     },
34175     
34176 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34177 //    {
34178 //        var pos = this.el.getBox(true);
34179 //        var x = pos.x;
34180 //        var y = pos.y;
34181 //        var maxX = pos.right;
34182 //        
34183 //        var maxHeight = 0;
34184 //        
34185 //        Roo.each(items, function(item, k){
34186 //            
34187 //            var c = k % 2;
34188 //            
34189 //            item.el.position('absolute');
34190 //                
34191 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34192 //
34193 //            item.el.setWidth(width);
34194 //
34195 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34196 //
34197 //            item.el.setHeight(height);
34198 //            
34199 //            if(c == 0){
34200 //                item.el.setXY([x, y], isInstant ? false : true);
34201 //            } else {
34202 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34203 //            }
34204 //            
34205 //            y = y + height + this.alternativePadWidth;
34206 //            
34207 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34208 //            
34209 //        }, this);
34210 //        
34211 //        this.el.setHeight(maxHeight);
34212 //        
34213 //    },
34214     
34215     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34216     {
34217         var pos = this.el.getBox(true);
34218         
34219         var minX = pos.x;
34220         var minY = pos.y;
34221         
34222         var maxX = pos.right;
34223         
34224         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34225         
34226         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34227         
34228         Roo.each(queue, function(box, k){
34229             
34230             Roo.each(box, function(b, kk){
34231                 
34232                 b.el.position('absolute');
34233                 
34234                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34235                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34236                 
34237                 if(b.size == 'md-left' || b.size == 'md-right'){
34238                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34239                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34240                 }
34241                 
34242                 b.el.setWidth(width);
34243                 b.el.setHeight(height);
34244                 
34245             }, this);
34246             
34247             if(!box.length){
34248                 return;
34249             }
34250             
34251             var positions = [];
34252             
34253             switch (box.length){
34254                 case 1 :
34255                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34256                     break;
34257                 case 2 :
34258                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34259                     break;
34260                 case 3 :
34261                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34262                     break;
34263                 case 4 :
34264                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34265                     break;
34266                 default :
34267                     break;
34268             }
34269             
34270             Roo.each(box, function(b,kk){
34271                 
34272                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34273                 
34274                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34275                 
34276             }, this);
34277             
34278         }, this);
34279         
34280     },
34281     
34282     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34283     {
34284         Roo.each(eItems, function(b,k){
34285             
34286             b.size = (k == 0) ? 'sm' : 'xs';
34287             b.x = (k == 0) ? 2 : 1;
34288             b.y = (k == 0) ? 2 : 1;
34289             
34290             b.el.position('absolute');
34291             
34292             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34293                 
34294             b.el.setWidth(width);
34295             
34296             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34297             
34298             b.el.setHeight(height);
34299             
34300         }, this);
34301
34302         var positions = [];
34303         
34304         positions.push({
34305             x : maxX - this.unitWidth * 2 - this.gutter,
34306             y : minY
34307         });
34308         
34309         positions.push({
34310             x : maxX - this.unitWidth,
34311             y : minY + (this.unitWidth + this.gutter) * 2
34312         });
34313         
34314         positions.push({
34315             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34316             y : minY
34317         });
34318         
34319         Roo.each(eItems, function(b,k){
34320             
34321             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34322
34323         }, this);
34324         
34325     },
34326     
34327     getVerticalOneBoxColPositions : function(x, y, box)
34328     {
34329         var pos = [];
34330         
34331         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34332         
34333         if(box[0].size == 'md-left'){
34334             rand = 0;
34335         }
34336         
34337         if(box[0].size == 'md-right'){
34338             rand = 1;
34339         }
34340         
34341         pos.push({
34342             x : x + (this.unitWidth + this.gutter) * rand,
34343             y : y
34344         });
34345         
34346         return pos;
34347     },
34348     
34349     getVerticalTwoBoxColPositions : function(x, y, box)
34350     {
34351         var pos = [];
34352         
34353         if(box[0].size == 'xs'){
34354             
34355             pos.push({
34356                 x : x,
34357                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34358             });
34359
34360             pos.push({
34361                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34362                 y : y
34363             });
34364             
34365             return pos;
34366             
34367         }
34368         
34369         pos.push({
34370             x : x,
34371             y : y
34372         });
34373
34374         pos.push({
34375             x : x + (this.unitWidth + this.gutter) * 2,
34376             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34377         });
34378         
34379         return pos;
34380         
34381     },
34382     
34383     getVerticalThreeBoxColPositions : function(x, y, box)
34384     {
34385         var pos = [];
34386         
34387         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34388             
34389             pos.push({
34390                 x : x,
34391                 y : y
34392             });
34393
34394             pos.push({
34395                 x : x + (this.unitWidth + this.gutter) * 1,
34396                 y : y
34397             });
34398             
34399             pos.push({
34400                 x : x + (this.unitWidth + this.gutter) * 2,
34401                 y : y
34402             });
34403             
34404             return pos;
34405             
34406         }
34407         
34408         if(box[0].size == 'xs' && box[1].size == 'xs'){
34409             
34410             pos.push({
34411                 x : x,
34412                 y : y
34413             });
34414
34415             pos.push({
34416                 x : x,
34417                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34418             });
34419             
34420             pos.push({
34421                 x : x + (this.unitWidth + this.gutter) * 1,
34422                 y : y
34423             });
34424             
34425             return pos;
34426             
34427         }
34428         
34429         pos.push({
34430             x : x,
34431             y : y
34432         });
34433
34434         pos.push({
34435             x : x + (this.unitWidth + this.gutter) * 2,
34436             y : y
34437         });
34438
34439         pos.push({
34440             x : x + (this.unitWidth + this.gutter) * 2,
34441             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34442         });
34443             
34444         return pos;
34445         
34446     },
34447     
34448     getVerticalFourBoxColPositions : function(x, y, box)
34449     {
34450         var pos = [];
34451         
34452         if(box[0].size == 'xs'){
34453             
34454             pos.push({
34455                 x : x,
34456                 y : y
34457             });
34458
34459             pos.push({
34460                 x : x,
34461                 y : y + (this.unitHeight + this.gutter) * 1
34462             });
34463             
34464             pos.push({
34465                 x : x,
34466                 y : y + (this.unitHeight + this.gutter) * 2
34467             });
34468             
34469             pos.push({
34470                 x : x + (this.unitWidth + this.gutter) * 1,
34471                 y : y
34472             });
34473             
34474             return pos;
34475             
34476         }
34477         
34478         pos.push({
34479             x : x,
34480             y : y
34481         });
34482
34483         pos.push({
34484             x : x + (this.unitWidth + this.gutter) * 2,
34485             y : y
34486         });
34487
34488         pos.push({
34489             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34490             y : y + (this.unitHeight + this.gutter) * 1
34491         });
34492
34493         pos.push({
34494             x : x + (this.unitWidth + this.gutter) * 2,
34495             y : y + (this.unitWidth + this.gutter) * 2
34496         });
34497
34498         return pos;
34499         
34500     },
34501     
34502     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34503     {
34504         var pos = [];
34505         
34506         if(box[0].size == 'md-left'){
34507             pos.push({
34508                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34509                 y : minY
34510             });
34511             
34512             return pos;
34513         }
34514         
34515         if(box[0].size == 'md-right'){
34516             pos.push({
34517                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34518                 y : minY + (this.unitWidth + this.gutter) * 1
34519             });
34520             
34521             return pos;
34522         }
34523         
34524         var rand = Math.floor(Math.random() * (4 - box[0].y));
34525         
34526         pos.push({
34527             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34528             y : minY + (this.unitWidth + this.gutter) * rand
34529         });
34530         
34531         return pos;
34532         
34533     },
34534     
34535     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34536     {
34537         var pos = [];
34538         
34539         if(box[0].size == 'xs'){
34540             
34541             pos.push({
34542                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34543                 y : minY
34544             });
34545
34546             pos.push({
34547                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34548                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34549             });
34550             
34551             return pos;
34552             
34553         }
34554         
34555         pos.push({
34556             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34557             y : minY
34558         });
34559
34560         pos.push({
34561             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34562             y : minY + (this.unitWidth + this.gutter) * 2
34563         });
34564         
34565         return pos;
34566         
34567     },
34568     
34569     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34570     {
34571         var pos = [];
34572         
34573         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34574             
34575             pos.push({
34576                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34577                 y : minY
34578             });
34579
34580             pos.push({
34581                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34582                 y : minY + (this.unitWidth + this.gutter) * 1
34583             });
34584             
34585             pos.push({
34586                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34587                 y : minY + (this.unitWidth + this.gutter) * 2
34588             });
34589             
34590             return pos;
34591             
34592         }
34593         
34594         if(box[0].size == 'xs' && box[1].size == 'xs'){
34595             
34596             pos.push({
34597                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34598                 y : minY
34599             });
34600
34601             pos.push({
34602                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34603                 y : minY
34604             });
34605             
34606             pos.push({
34607                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34608                 y : minY + (this.unitWidth + this.gutter) * 1
34609             });
34610             
34611             return pos;
34612             
34613         }
34614         
34615         pos.push({
34616             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34617             y : minY
34618         });
34619
34620         pos.push({
34621             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34622             y : minY + (this.unitWidth + this.gutter) * 2
34623         });
34624
34625         pos.push({
34626             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34627             y : minY + (this.unitWidth + this.gutter) * 2
34628         });
34629             
34630         return pos;
34631         
34632     },
34633     
34634     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34635     {
34636         var pos = [];
34637         
34638         if(box[0].size == 'xs'){
34639             
34640             pos.push({
34641                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34642                 y : minY
34643             });
34644
34645             pos.push({
34646                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34647                 y : minY
34648             });
34649             
34650             pos.push({
34651                 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),
34652                 y : minY
34653             });
34654             
34655             pos.push({
34656                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34657                 y : minY + (this.unitWidth + this.gutter) * 1
34658             });
34659             
34660             return pos;
34661             
34662         }
34663         
34664         pos.push({
34665             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34666             y : minY
34667         });
34668         
34669         pos.push({
34670             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34671             y : minY + (this.unitWidth + this.gutter) * 2
34672         });
34673         
34674         pos.push({
34675             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34676             y : minY + (this.unitWidth + this.gutter) * 2
34677         });
34678         
34679         pos.push({
34680             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),
34681             y : minY + (this.unitWidth + this.gutter) * 2
34682         });
34683
34684         return pos;
34685         
34686     },
34687     
34688     /**
34689     * remove a Masonry Brick
34690     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34691     */
34692     removeBrick : function(brick_id)
34693     {
34694         if (!brick_id) {
34695             return;
34696         }
34697         
34698         for (var i = 0; i<this.bricks.length; i++) {
34699             if (this.bricks[i].id == brick_id) {
34700                 this.bricks.splice(i,1);
34701                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34702                 this.initial();
34703             }
34704         }
34705     },
34706     
34707     /**
34708     * adds a Masonry Brick
34709     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34710     */
34711     addBrick : function(cfg)
34712     {
34713         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34714         //this.register(cn);
34715         cn.parentId = this.id;
34716         cn.render(this.el);
34717         return cn;
34718     },
34719     
34720     /**
34721     * register a Masonry Brick
34722     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34723     */
34724     
34725     register : function(brick)
34726     {
34727         this.bricks.push(brick);
34728         brick.masonryId = this.id;
34729     },
34730     
34731     /**
34732     * clear all the Masonry Brick
34733     */
34734     clearAll : function()
34735     {
34736         this.bricks = [];
34737         //this.getChildContainer().dom.innerHTML = "";
34738         this.el.dom.innerHTML = '';
34739     },
34740     
34741     getSelected : function()
34742     {
34743         if (!this.selectedBrick) {
34744             return false;
34745         }
34746         
34747         return this.selectedBrick;
34748     }
34749 });
34750
34751 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34752     
34753     groups: {},
34754      /**
34755     * register a Masonry Layout
34756     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34757     */
34758     
34759     register : function(layout)
34760     {
34761         this.groups[layout.id] = layout;
34762     },
34763     /**
34764     * fetch a  Masonry Layout based on the masonry layout ID
34765     * @param {string} the masonry layout to add
34766     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34767     */
34768     
34769     get: function(layout_id) {
34770         if (typeof(this.groups[layout_id]) == 'undefined') {
34771             return false;
34772         }
34773         return this.groups[layout_id] ;
34774     }
34775     
34776     
34777     
34778 });
34779
34780  
34781
34782  /**
34783  *
34784  * This is based on 
34785  * http://masonry.desandro.com
34786  *
34787  * The idea is to render all the bricks based on vertical width...
34788  *
34789  * The original code extends 'outlayer' - we might need to use that....
34790  * 
34791  */
34792
34793
34794 /**
34795  * @class Roo.bootstrap.LayoutMasonryAuto
34796  * @extends Roo.bootstrap.Component
34797  * Bootstrap Layout Masonry class
34798  * 
34799  * @constructor
34800  * Create a new Element
34801  * @param {Object} config The config object
34802  */
34803
34804 Roo.bootstrap.LayoutMasonryAuto = function(config){
34805     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34806 };
34807
34808 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34809     
34810       /**
34811      * @cfg {Boolean} isFitWidth  - resize the width..
34812      */   
34813     isFitWidth : false,  // options..
34814     /**
34815      * @cfg {Boolean} isOriginLeft = left align?
34816      */   
34817     isOriginLeft : true,
34818     /**
34819      * @cfg {Boolean} isOriginTop = top align?
34820      */   
34821     isOriginTop : false,
34822     /**
34823      * @cfg {Boolean} isLayoutInstant = no animation?
34824      */   
34825     isLayoutInstant : false, // needed?
34826     /**
34827      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34828      */   
34829     isResizingContainer : true,
34830     /**
34831      * @cfg {Number} columnWidth  width of the columns 
34832      */   
34833     
34834     columnWidth : 0,
34835     
34836     /**
34837      * @cfg {Number} maxCols maximum number of columns
34838      */   
34839     
34840     maxCols: 0,
34841     /**
34842      * @cfg {Number} padHeight padding below box..
34843      */   
34844     
34845     padHeight : 10, 
34846     
34847     /**
34848      * @cfg {Boolean} isAutoInitial defalut true
34849      */   
34850     
34851     isAutoInitial : true, 
34852     
34853     // private?
34854     gutter : 0,
34855     
34856     containerWidth: 0,
34857     initialColumnWidth : 0,
34858     currentSize : null,
34859     
34860     colYs : null, // array.
34861     maxY : 0,
34862     padWidth: 10,
34863     
34864     
34865     tag: 'div',
34866     cls: '',
34867     bricks: null, //CompositeElement
34868     cols : 0, // array?
34869     // element : null, // wrapped now this.el
34870     _isLayoutInited : null, 
34871     
34872     
34873     getAutoCreate : function(){
34874         
34875         var cfg = {
34876             tag: this.tag,
34877             cls: 'blog-masonary-wrapper ' + this.cls,
34878             cn : {
34879                 cls : 'mas-boxes masonary'
34880             }
34881         };
34882         
34883         return cfg;
34884     },
34885     
34886     getChildContainer: function( )
34887     {
34888         if (this.boxesEl) {
34889             return this.boxesEl;
34890         }
34891         
34892         this.boxesEl = this.el.select('.mas-boxes').first();
34893         
34894         return this.boxesEl;
34895     },
34896     
34897     
34898     initEvents : function()
34899     {
34900         var _this = this;
34901         
34902         if(this.isAutoInitial){
34903             Roo.log('hook children rendered');
34904             this.on('childrenrendered', function() {
34905                 Roo.log('children rendered');
34906                 _this.initial();
34907             } ,this);
34908         }
34909         
34910     },
34911     
34912     initial : function()
34913     {
34914         this.reloadItems();
34915
34916         this.currentSize = this.el.getBox(true);
34917
34918         /// was window resize... - let's see if this works..
34919         Roo.EventManager.onWindowResize(this.resize, this); 
34920
34921         if(!this.isAutoInitial){
34922             this.layout();
34923             return;
34924         }
34925         
34926         this.layout.defer(500,this);
34927     },
34928     
34929     reloadItems: function()
34930     {
34931         this.bricks = this.el.select('.masonry-brick', true);
34932         
34933         this.bricks.each(function(b) {
34934             //Roo.log(b.getSize());
34935             if (!b.attr('originalwidth')) {
34936                 b.attr('originalwidth',  b.getSize().width);
34937             }
34938             
34939         });
34940         
34941         Roo.log(this.bricks.elements.length);
34942     },
34943     
34944     resize : function()
34945     {
34946         Roo.log('resize');
34947         var cs = this.el.getBox(true);
34948         
34949         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34950             Roo.log("no change in with or X");
34951             return;
34952         }
34953         this.currentSize = cs;
34954         this.layout();
34955     },
34956     
34957     layout : function()
34958     {
34959          Roo.log('layout');
34960         this._resetLayout();
34961         //this._manageStamps();
34962       
34963         // don't animate first layout
34964         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34965         this.layoutItems( isInstant );
34966       
34967         // flag for initalized
34968         this._isLayoutInited = true;
34969     },
34970     
34971     layoutItems : function( isInstant )
34972     {
34973         //var items = this._getItemsForLayout( this.items );
34974         // original code supports filtering layout items.. we just ignore it..
34975         
34976         this._layoutItems( this.bricks , isInstant );
34977       
34978         this._postLayout();
34979     },
34980     _layoutItems : function ( items , isInstant)
34981     {
34982        //this.fireEvent( 'layout', this, items );
34983     
34984
34985         if ( !items || !items.elements.length ) {
34986           // no items, emit event with empty array
34987             return;
34988         }
34989
34990         var queue = [];
34991         items.each(function(item) {
34992             Roo.log("layout item");
34993             Roo.log(item);
34994             // get x/y object from method
34995             var position = this._getItemLayoutPosition( item );
34996             // enqueue
34997             position.item = item;
34998             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34999             queue.push( position );
35000         }, this);
35001       
35002         this._processLayoutQueue( queue );
35003     },
35004     /** Sets position of item in DOM
35005     * @param {Element} item
35006     * @param {Number} x - horizontal position
35007     * @param {Number} y - vertical position
35008     * @param {Boolean} isInstant - disables transitions
35009     */
35010     _processLayoutQueue : function( queue )
35011     {
35012         for ( var i=0, len = queue.length; i < len; i++ ) {
35013             var obj = queue[i];
35014             obj.item.position('absolute');
35015             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35016         }
35017     },
35018       
35019     
35020     /**
35021     * Any logic you want to do after each layout,
35022     * i.e. size the container
35023     */
35024     _postLayout : function()
35025     {
35026         this.resizeContainer();
35027     },
35028     
35029     resizeContainer : function()
35030     {
35031         if ( !this.isResizingContainer ) {
35032             return;
35033         }
35034         var size = this._getContainerSize();
35035         if ( size ) {
35036             this.el.setSize(size.width,size.height);
35037             this.boxesEl.setSize(size.width,size.height);
35038         }
35039     },
35040     
35041     
35042     
35043     _resetLayout : function()
35044     {
35045         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35046         this.colWidth = this.el.getWidth();
35047         //this.gutter = this.el.getWidth(); 
35048         
35049         this.measureColumns();
35050
35051         // reset column Y
35052         var i = this.cols;
35053         this.colYs = [];
35054         while (i--) {
35055             this.colYs.push( 0 );
35056         }
35057     
35058         this.maxY = 0;
35059     },
35060
35061     measureColumns : function()
35062     {
35063         this.getContainerWidth();
35064       // if columnWidth is 0, default to outerWidth of first item
35065         if ( !this.columnWidth ) {
35066             var firstItem = this.bricks.first();
35067             Roo.log(firstItem);
35068             this.columnWidth  = this.containerWidth;
35069             if (firstItem && firstItem.attr('originalwidth') ) {
35070                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35071             }
35072             // columnWidth fall back to item of first element
35073             Roo.log("set column width?");
35074                         this.initialColumnWidth = this.columnWidth  ;
35075
35076             // if first elem has no width, default to size of container
35077             
35078         }
35079         
35080         
35081         if (this.initialColumnWidth) {
35082             this.columnWidth = this.initialColumnWidth;
35083         }
35084         
35085         
35086             
35087         // column width is fixed at the top - however if container width get's smaller we should
35088         // reduce it...
35089         
35090         // this bit calcs how man columns..
35091             
35092         var columnWidth = this.columnWidth += this.gutter;
35093       
35094         // calculate columns
35095         var containerWidth = this.containerWidth + this.gutter;
35096         
35097         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35098         // fix rounding errors, typically with gutters
35099         var excess = columnWidth - containerWidth % columnWidth;
35100         
35101         
35102         // if overshoot is less than a pixel, round up, otherwise floor it
35103         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35104         cols = Math[ mathMethod ]( cols );
35105         this.cols = Math.max( cols, 1 );
35106         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35107         
35108          // padding positioning..
35109         var totalColWidth = this.cols * this.columnWidth;
35110         var padavail = this.containerWidth - totalColWidth;
35111         // so for 2 columns - we need 3 'pads'
35112         
35113         var padNeeded = (1+this.cols) * this.padWidth;
35114         
35115         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35116         
35117         this.columnWidth += padExtra
35118         //this.padWidth = Math.floor(padavail /  ( this.cols));
35119         
35120         // adjust colum width so that padding is fixed??
35121         
35122         // we have 3 columns ... total = width * 3
35123         // we have X left over... that should be used by 
35124         
35125         //if (this.expandC) {
35126             
35127         //}
35128         
35129         
35130         
35131     },
35132     
35133     getContainerWidth : function()
35134     {
35135        /* // container is parent if fit width
35136         var container = this.isFitWidth ? this.element.parentNode : this.element;
35137         // check that this.size and size are there
35138         // IE8 triggers resize on body size change, so they might not be
35139         
35140         var size = getSize( container );  //FIXME
35141         this.containerWidth = size && size.innerWidth; //FIXME
35142         */
35143          
35144         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35145         
35146     },
35147     
35148     _getItemLayoutPosition : function( item )  // what is item?
35149     {
35150         // we resize the item to our columnWidth..
35151       
35152         item.setWidth(this.columnWidth);
35153         item.autoBoxAdjust  = false;
35154         
35155         var sz = item.getSize();
35156  
35157         // how many columns does this brick span
35158         var remainder = this.containerWidth % this.columnWidth;
35159         
35160         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35161         // round if off by 1 pixel, otherwise use ceil
35162         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35163         colSpan = Math.min( colSpan, this.cols );
35164         
35165         // normally this should be '1' as we dont' currently allow multi width columns..
35166         
35167         var colGroup = this._getColGroup( colSpan );
35168         // get the minimum Y value from the columns
35169         var minimumY = Math.min.apply( Math, colGroup );
35170         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35171         
35172         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35173          
35174         // position the brick
35175         var position = {
35176             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35177             y: this.currentSize.y + minimumY + this.padHeight
35178         };
35179         
35180         Roo.log(position);
35181         // apply setHeight to necessary columns
35182         var setHeight = minimumY + sz.height + this.padHeight;
35183         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35184         
35185         var setSpan = this.cols + 1 - colGroup.length;
35186         for ( var i = 0; i < setSpan; i++ ) {
35187           this.colYs[ shortColIndex + i ] = setHeight ;
35188         }
35189       
35190         return position;
35191     },
35192     
35193     /**
35194      * @param {Number} colSpan - number of columns the element spans
35195      * @returns {Array} colGroup
35196      */
35197     _getColGroup : function( colSpan )
35198     {
35199         if ( colSpan < 2 ) {
35200           // if brick spans only one column, use all the column Ys
35201           return this.colYs;
35202         }
35203       
35204         var colGroup = [];
35205         // how many different places could this brick fit horizontally
35206         var groupCount = this.cols + 1 - colSpan;
35207         // for each group potential horizontal position
35208         for ( var i = 0; i < groupCount; i++ ) {
35209           // make an array of colY values for that one group
35210           var groupColYs = this.colYs.slice( i, i + colSpan );
35211           // and get the max value of the array
35212           colGroup[i] = Math.max.apply( Math, groupColYs );
35213         }
35214         return colGroup;
35215     },
35216     /*
35217     _manageStamp : function( stamp )
35218     {
35219         var stampSize =  stamp.getSize();
35220         var offset = stamp.getBox();
35221         // get the columns that this stamp affects
35222         var firstX = this.isOriginLeft ? offset.x : offset.right;
35223         var lastX = firstX + stampSize.width;
35224         var firstCol = Math.floor( firstX / this.columnWidth );
35225         firstCol = Math.max( 0, firstCol );
35226         
35227         var lastCol = Math.floor( lastX / this.columnWidth );
35228         // lastCol should not go over if multiple of columnWidth #425
35229         lastCol -= lastX % this.columnWidth ? 0 : 1;
35230         lastCol = Math.min( this.cols - 1, lastCol );
35231         
35232         // set colYs to bottom of the stamp
35233         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35234             stampSize.height;
35235             
35236         for ( var i = firstCol; i <= lastCol; i++ ) {
35237           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35238         }
35239     },
35240     */
35241     
35242     _getContainerSize : function()
35243     {
35244         this.maxY = Math.max.apply( Math, this.colYs );
35245         var size = {
35246             height: this.maxY
35247         };
35248       
35249         if ( this.isFitWidth ) {
35250             size.width = this._getContainerFitWidth();
35251         }
35252       
35253         return size;
35254     },
35255     
35256     _getContainerFitWidth : function()
35257     {
35258         var unusedCols = 0;
35259         // count unused columns
35260         var i = this.cols;
35261         while ( --i ) {
35262           if ( this.colYs[i] !== 0 ) {
35263             break;
35264           }
35265           unusedCols++;
35266         }
35267         // fit container to columns that have been used
35268         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35269     },
35270     
35271     needsResizeLayout : function()
35272     {
35273         var previousWidth = this.containerWidth;
35274         this.getContainerWidth();
35275         return previousWidth !== this.containerWidth;
35276     }
35277  
35278 });
35279
35280  
35281
35282  /*
35283  * - LGPL
35284  *
35285  * element
35286  * 
35287  */
35288
35289 /**
35290  * @class Roo.bootstrap.MasonryBrick
35291  * @extends Roo.bootstrap.Component
35292  * Bootstrap MasonryBrick class
35293  * 
35294  * @constructor
35295  * Create a new MasonryBrick
35296  * @param {Object} config The config object
35297  */
35298
35299 Roo.bootstrap.MasonryBrick = function(config){
35300     
35301     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35302     
35303     Roo.bootstrap.MasonryBrick.register(this);
35304     
35305     this.addEvents({
35306         // raw events
35307         /**
35308          * @event click
35309          * When a MasonryBrick is clcik
35310          * @param {Roo.bootstrap.MasonryBrick} this
35311          * @param {Roo.EventObject} e
35312          */
35313         "click" : true
35314     });
35315 };
35316
35317 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35318     
35319     /**
35320      * @cfg {String} title
35321      */   
35322     title : '',
35323     /**
35324      * @cfg {String} html
35325      */   
35326     html : '',
35327     /**
35328      * @cfg {String} bgimage
35329      */   
35330     bgimage : '',
35331     /**
35332      * @cfg {String} videourl
35333      */   
35334     videourl : '',
35335     /**
35336      * @cfg {String} cls
35337      */   
35338     cls : '',
35339     /**
35340      * @cfg {String} href
35341      */   
35342     href : '',
35343     /**
35344      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35345      */   
35346     size : 'xs',
35347     
35348     /**
35349      * @cfg {String} placetitle (center|bottom)
35350      */   
35351     placetitle : '',
35352     
35353     /**
35354      * @cfg {Boolean} isFitContainer defalut true
35355      */   
35356     isFitContainer : true, 
35357     
35358     /**
35359      * @cfg {Boolean} preventDefault defalut false
35360      */   
35361     preventDefault : false, 
35362     
35363     /**
35364      * @cfg {Boolean} inverse defalut false
35365      */   
35366     maskInverse : false, 
35367     
35368     getAutoCreate : function()
35369     {
35370         if(!this.isFitContainer){
35371             return this.getSplitAutoCreate();
35372         }
35373         
35374         var cls = 'masonry-brick masonry-brick-full';
35375         
35376         if(this.href.length){
35377             cls += ' masonry-brick-link';
35378         }
35379         
35380         if(this.bgimage.length){
35381             cls += ' masonry-brick-image';
35382         }
35383         
35384         if(this.maskInverse){
35385             cls += ' mask-inverse';
35386         }
35387         
35388         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35389             cls += ' enable-mask';
35390         }
35391         
35392         if(this.size){
35393             cls += ' masonry-' + this.size + '-brick';
35394         }
35395         
35396         if(this.placetitle.length){
35397             
35398             switch (this.placetitle) {
35399                 case 'center' :
35400                     cls += ' masonry-center-title';
35401                     break;
35402                 case 'bottom' :
35403                     cls += ' masonry-bottom-title';
35404                     break;
35405                 default:
35406                     break;
35407             }
35408             
35409         } else {
35410             if(!this.html.length && !this.bgimage.length){
35411                 cls += ' masonry-center-title';
35412             }
35413
35414             if(!this.html.length && this.bgimage.length){
35415                 cls += ' masonry-bottom-title';
35416             }
35417         }
35418         
35419         if(this.cls){
35420             cls += ' ' + this.cls;
35421         }
35422         
35423         var cfg = {
35424             tag: (this.href.length) ? 'a' : 'div',
35425             cls: cls,
35426             cn: [
35427                 {
35428                     tag: 'div',
35429                     cls: 'masonry-brick-mask'
35430                 },
35431                 {
35432                     tag: 'div',
35433                     cls: 'masonry-brick-paragraph',
35434                     cn: []
35435                 }
35436             ]
35437         };
35438         
35439         if(this.href.length){
35440             cfg.href = this.href;
35441         }
35442         
35443         var cn = cfg.cn[1].cn;
35444         
35445         if(this.title.length){
35446             cn.push({
35447                 tag: 'h4',
35448                 cls: 'masonry-brick-title',
35449                 html: this.title
35450             });
35451         }
35452         
35453         if(this.html.length){
35454             cn.push({
35455                 tag: 'p',
35456                 cls: 'masonry-brick-text',
35457                 html: this.html
35458             });
35459         }
35460         
35461         if (!this.title.length && !this.html.length) {
35462             cfg.cn[1].cls += ' hide';
35463         }
35464         
35465         if(this.bgimage.length){
35466             cfg.cn.push({
35467                 tag: 'img',
35468                 cls: 'masonry-brick-image-view',
35469                 src: this.bgimage
35470             });
35471         }
35472         
35473         if(this.videourl.length){
35474             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35475             // youtube support only?
35476             cfg.cn.push({
35477                 tag: 'iframe',
35478                 cls: 'masonry-brick-image-view',
35479                 src: vurl,
35480                 frameborder : 0,
35481                 allowfullscreen : true
35482             });
35483         }
35484         
35485         return cfg;
35486         
35487     },
35488     
35489     getSplitAutoCreate : function()
35490     {
35491         var cls = 'masonry-brick masonry-brick-split';
35492         
35493         if(this.href.length){
35494             cls += ' masonry-brick-link';
35495         }
35496         
35497         if(this.bgimage.length){
35498             cls += ' masonry-brick-image';
35499         }
35500         
35501         if(this.size){
35502             cls += ' masonry-' + this.size + '-brick';
35503         }
35504         
35505         switch (this.placetitle) {
35506             case 'center' :
35507                 cls += ' masonry-center-title';
35508                 break;
35509             case 'bottom' :
35510                 cls += ' masonry-bottom-title';
35511                 break;
35512             default:
35513                 if(!this.bgimage.length){
35514                     cls += ' masonry-center-title';
35515                 }
35516
35517                 if(this.bgimage.length){
35518                     cls += ' masonry-bottom-title';
35519                 }
35520                 break;
35521         }
35522         
35523         if(this.cls){
35524             cls += ' ' + this.cls;
35525         }
35526         
35527         var cfg = {
35528             tag: (this.href.length) ? 'a' : 'div',
35529             cls: cls,
35530             cn: [
35531                 {
35532                     tag: 'div',
35533                     cls: 'masonry-brick-split-head',
35534                     cn: [
35535                         {
35536                             tag: 'div',
35537                             cls: 'masonry-brick-paragraph',
35538                             cn: []
35539                         }
35540                     ]
35541                 },
35542                 {
35543                     tag: 'div',
35544                     cls: 'masonry-brick-split-body',
35545                     cn: []
35546                 }
35547             ]
35548         };
35549         
35550         if(this.href.length){
35551             cfg.href = this.href;
35552         }
35553         
35554         if(this.title.length){
35555             cfg.cn[0].cn[0].cn.push({
35556                 tag: 'h4',
35557                 cls: 'masonry-brick-title',
35558                 html: this.title
35559             });
35560         }
35561         
35562         if(this.html.length){
35563             cfg.cn[1].cn.push({
35564                 tag: 'p',
35565                 cls: 'masonry-brick-text',
35566                 html: this.html
35567             });
35568         }
35569
35570         if(this.bgimage.length){
35571             cfg.cn[0].cn.push({
35572                 tag: 'img',
35573                 cls: 'masonry-brick-image-view',
35574                 src: this.bgimage
35575             });
35576         }
35577         
35578         if(this.videourl.length){
35579             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35580             // youtube support only?
35581             cfg.cn[0].cn.cn.push({
35582                 tag: 'iframe',
35583                 cls: 'masonry-brick-image-view',
35584                 src: vurl,
35585                 frameborder : 0,
35586                 allowfullscreen : true
35587             });
35588         }
35589         
35590         return cfg;
35591     },
35592     
35593     initEvents: function() 
35594     {
35595         switch (this.size) {
35596             case 'xs' :
35597                 this.x = 1;
35598                 this.y = 1;
35599                 break;
35600             case 'sm' :
35601                 this.x = 2;
35602                 this.y = 2;
35603                 break;
35604             case 'md' :
35605             case 'md-left' :
35606             case 'md-right' :
35607                 this.x = 3;
35608                 this.y = 3;
35609                 break;
35610             case 'tall' :
35611                 this.x = 2;
35612                 this.y = 3;
35613                 break;
35614             case 'wide' :
35615                 this.x = 3;
35616                 this.y = 2;
35617                 break;
35618             case 'wide-thin' :
35619                 this.x = 3;
35620                 this.y = 1;
35621                 break;
35622                         
35623             default :
35624                 break;
35625         }
35626         
35627         if(Roo.isTouch){
35628             this.el.on('touchstart', this.onTouchStart, this);
35629             this.el.on('touchmove', this.onTouchMove, this);
35630             this.el.on('touchend', this.onTouchEnd, this);
35631             this.el.on('contextmenu', this.onContextMenu, this);
35632         } else {
35633             this.el.on('mouseenter'  ,this.enter, this);
35634             this.el.on('mouseleave', this.leave, this);
35635             this.el.on('click', this.onClick, this);
35636         }
35637         
35638         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35639             this.parent().bricks.push(this);   
35640         }
35641         
35642     },
35643     
35644     onClick: function(e, el)
35645     {
35646         var time = this.endTimer - this.startTimer;
35647         // Roo.log(e.preventDefault());
35648         if(Roo.isTouch){
35649             if(time > 1000){
35650                 e.preventDefault();
35651                 return;
35652             }
35653         }
35654         
35655         if(!this.preventDefault){
35656             return;
35657         }
35658         
35659         e.preventDefault();
35660         
35661         if (this.activeClass != '') {
35662             this.selectBrick();
35663         }
35664         
35665         this.fireEvent('click', this, e);
35666     },
35667     
35668     enter: function(e, el)
35669     {
35670         e.preventDefault();
35671         
35672         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35673             return;
35674         }
35675         
35676         if(this.bgimage.length && this.html.length){
35677             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35678         }
35679     },
35680     
35681     leave: function(e, el)
35682     {
35683         e.preventDefault();
35684         
35685         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35686             return;
35687         }
35688         
35689         if(this.bgimage.length && this.html.length){
35690             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35691         }
35692     },
35693     
35694     onTouchStart: function(e, el)
35695     {
35696 //        e.preventDefault();
35697         
35698         this.touchmoved = false;
35699         
35700         if(!this.isFitContainer){
35701             return;
35702         }
35703         
35704         if(!this.bgimage.length || !this.html.length){
35705             return;
35706         }
35707         
35708         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35709         
35710         this.timer = new Date().getTime();
35711         
35712     },
35713     
35714     onTouchMove: function(e, el)
35715     {
35716         this.touchmoved = true;
35717     },
35718     
35719     onContextMenu : function(e,el)
35720     {
35721         e.preventDefault();
35722         e.stopPropagation();
35723         return false;
35724     },
35725     
35726     onTouchEnd: function(e, el)
35727     {
35728 //        e.preventDefault();
35729         
35730         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35731         
35732             this.leave(e,el);
35733             
35734             return;
35735         }
35736         
35737         if(!this.bgimage.length || !this.html.length){
35738             
35739             if(this.href.length){
35740                 window.location.href = this.href;
35741             }
35742             
35743             return;
35744         }
35745         
35746         if(!this.isFitContainer){
35747             return;
35748         }
35749         
35750         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35751         
35752         window.location.href = this.href;
35753     },
35754     
35755     //selection on single brick only
35756     selectBrick : function() {
35757         
35758         if (!this.parentId) {
35759             return;
35760         }
35761         
35762         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35763         var index = m.selectedBrick.indexOf(this.id);
35764         
35765         if ( index > -1) {
35766             m.selectedBrick.splice(index,1);
35767             this.el.removeClass(this.activeClass);
35768             return;
35769         }
35770         
35771         for(var i = 0; i < m.selectedBrick.length; i++) {
35772             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35773             b.el.removeClass(b.activeClass);
35774         }
35775         
35776         m.selectedBrick = [];
35777         
35778         m.selectedBrick.push(this.id);
35779         this.el.addClass(this.activeClass);
35780         return;
35781     },
35782     
35783     isSelected : function(){
35784         return this.el.hasClass(this.activeClass);
35785         
35786     }
35787 });
35788
35789 Roo.apply(Roo.bootstrap.MasonryBrick, {
35790     
35791     //groups: {},
35792     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35793      /**
35794     * register a Masonry Brick
35795     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35796     */
35797     
35798     register : function(brick)
35799     {
35800         //this.groups[brick.id] = brick;
35801         this.groups.add(brick.id, brick);
35802     },
35803     /**
35804     * fetch a  masonry brick based on the masonry brick ID
35805     * @param {string} the masonry brick to add
35806     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35807     */
35808     
35809     get: function(brick_id) 
35810     {
35811         // if (typeof(this.groups[brick_id]) == 'undefined') {
35812         //     return false;
35813         // }
35814         // return this.groups[brick_id] ;
35815         
35816         if(this.groups.key(brick_id)) {
35817             return this.groups.key(brick_id);
35818         }
35819         
35820         return false;
35821     }
35822     
35823     
35824     
35825 });
35826
35827  /*
35828  * - LGPL
35829  *
35830  * element
35831  * 
35832  */
35833
35834 /**
35835  * @class Roo.bootstrap.Brick
35836  * @extends Roo.bootstrap.Component
35837  * Bootstrap Brick class
35838  * 
35839  * @constructor
35840  * Create a new Brick
35841  * @param {Object} config The config object
35842  */
35843
35844 Roo.bootstrap.Brick = function(config){
35845     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35846     
35847     this.addEvents({
35848         // raw events
35849         /**
35850          * @event click
35851          * When a Brick is click
35852          * @param {Roo.bootstrap.Brick} this
35853          * @param {Roo.EventObject} e
35854          */
35855         "click" : true
35856     });
35857 };
35858
35859 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35860     
35861     /**
35862      * @cfg {String} title
35863      */   
35864     title : '',
35865     /**
35866      * @cfg {String} html
35867      */   
35868     html : '',
35869     /**
35870      * @cfg {String} bgimage
35871      */   
35872     bgimage : '',
35873     /**
35874      * @cfg {String} cls
35875      */   
35876     cls : '',
35877     /**
35878      * @cfg {String} href
35879      */   
35880     href : '',
35881     /**
35882      * @cfg {String} video
35883      */   
35884     video : '',
35885     /**
35886      * @cfg {Boolean} square
35887      */   
35888     square : true,
35889     
35890     getAutoCreate : function()
35891     {
35892         var cls = 'roo-brick';
35893         
35894         if(this.href.length){
35895             cls += ' roo-brick-link';
35896         }
35897         
35898         if(this.bgimage.length){
35899             cls += ' roo-brick-image';
35900         }
35901         
35902         if(!this.html.length && !this.bgimage.length){
35903             cls += ' roo-brick-center-title';
35904         }
35905         
35906         if(!this.html.length && this.bgimage.length){
35907             cls += ' roo-brick-bottom-title';
35908         }
35909         
35910         if(this.cls){
35911             cls += ' ' + this.cls;
35912         }
35913         
35914         var cfg = {
35915             tag: (this.href.length) ? 'a' : 'div',
35916             cls: cls,
35917             cn: [
35918                 {
35919                     tag: 'div',
35920                     cls: 'roo-brick-paragraph',
35921                     cn: []
35922                 }
35923             ]
35924         };
35925         
35926         if(this.href.length){
35927             cfg.href = this.href;
35928         }
35929         
35930         var cn = cfg.cn[0].cn;
35931         
35932         if(this.title.length){
35933             cn.push({
35934                 tag: 'h4',
35935                 cls: 'roo-brick-title',
35936                 html: this.title
35937             });
35938         }
35939         
35940         if(this.html.length){
35941             cn.push({
35942                 tag: 'p',
35943                 cls: 'roo-brick-text',
35944                 html: this.html
35945             });
35946         } else {
35947             cn.cls += ' hide';
35948         }
35949         
35950         if(this.bgimage.length){
35951             cfg.cn.push({
35952                 tag: 'img',
35953                 cls: 'roo-brick-image-view',
35954                 src: this.bgimage
35955             });
35956         }
35957         
35958         return cfg;
35959     },
35960     
35961     initEvents: function() 
35962     {
35963         if(this.title.length || this.html.length){
35964             this.el.on('mouseenter'  ,this.enter, this);
35965             this.el.on('mouseleave', this.leave, this);
35966         }
35967         
35968         Roo.EventManager.onWindowResize(this.resize, this); 
35969         
35970         if(this.bgimage.length){
35971             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35972             this.imageEl.on('load', this.onImageLoad, this);
35973             return;
35974         }
35975         
35976         this.resize();
35977     },
35978     
35979     onImageLoad : function()
35980     {
35981         this.resize();
35982     },
35983     
35984     resize : function()
35985     {
35986         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35987         
35988         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35989         
35990         if(this.bgimage.length){
35991             var image = this.el.select('.roo-brick-image-view', true).first();
35992             
35993             image.setWidth(paragraph.getWidth());
35994             
35995             if(this.square){
35996                 image.setHeight(paragraph.getWidth());
35997             }
35998             
35999             this.el.setHeight(image.getHeight());
36000             paragraph.setHeight(image.getHeight());
36001             
36002         }
36003         
36004     },
36005     
36006     enter: function(e, el)
36007     {
36008         e.preventDefault();
36009         
36010         if(this.bgimage.length){
36011             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36012             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36013         }
36014     },
36015     
36016     leave: function(e, el)
36017     {
36018         e.preventDefault();
36019         
36020         if(this.bgimage.length){
36021             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36022             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36023         }
36024     }
36025     
36026 });
36027
36028  
36029
36030  /*
36031  * - LGPL
36032  *
36033  * Number field 
36034  */
36035
36036 /**
36037  * @class Roo.bootstrap.NumberField
36038  * @extends Roo.bootstrap.Input
36039  * Bootstrap NumberField class
36040  * 
36041  * 
36042  * 
36043  * 
36044  * @constructor
36045  * Create a new NumberField
36046  * @param {Object} config The config object
36047  */
36048
36049 Roo.bootstrap.NumberField = function(config){
36050     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36051 };
36052
36053 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36054     
36055     /**
36056      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36057      */
36058     allowDecimals : true,
36059     /**
36060      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36061      */
36062     decimalSeparator : ".",
36063     /**
36064      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36065      */
36066     decimalPrecision : 2,
36067     /**
36068      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36069      */
36070     allowNegative : true,
36071     
36072     /**
36073      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36074      */
36075     allowZero: true,
36076     /**
36077      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36078      */
36079     minValue : Number.NEGATIVE_INFINITY,
36080     /**
36081      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36082      */
36083     maxValue : Number.MAX_VALUE,
36084     /**
36085      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36086      */
36087     minText : "The minimum value for this field is {0}",
36088     /**
36089      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36090      */
36091     maxText : "The maximum value for this field is {0}",
36092     /**
36093      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36094      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36095      */
36096     nanText : "{0} is not a valid number",
36097     /**
36098      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36099      */
36100     thousandsDelimiter : false,
36101     /**
36102      * @cfg {String} valueAlign alignment of value
36103      */
36104     valueAlign : "left",
36105
36106     getAutoCreate : function()
36107     {
36108         var hiddenInput = {
36109             tag: 'input',
36110             type: 'hidden',
36111             id: Roo.id(),
36112             cls: 'hidden-number-input'
36113         };
36114         
36115         if (this.name) {
36116             hiddenInput.name = this.name;
36117         }
36118         
36119         this.name = '';
36120         
36121         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36122         
36123         this.name = hiddenInput.name;
36124         
36125         if(cfg.cn.length > 0) {
36126             cfg.cn.push(hiddenInput);
36127         }
36128         
36129         return cfg;
36130     },
36131
36132     // private
36133     initEvents : function()
36134     {   
36135         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36136         
36137         var allowed = "0123456789";
36138         
36139         if(this.allowDecimals){
36140             allowed += this.decimalSeparator;
36141         }
36142         
36143         if(this.allowNegative){
36144             allowed += "-";
36145         }
36146         
36147         if(this.thousandsDelimiter) {
36148             allowed += ",";
36149         }
36150         
36151         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36152         
36153         var keyPress = function(e){
36154             
36155             var k = e.getKey();
36156             
36157             var c = e.getCharCode();
36158             
36159             if(
36160                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36161                     allowed.indexOf(String.fromCharCode(c)) === -1
36162             ){
36163                 e.stopEvent();
36164                 return;
36165             }
36166             
36167             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36168                 return;
36169             }
36170             
36171             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36172                 e.stopEvent();
36173             }
36174         };
36175         
36176         this.el.on("keypress", keyPress, this);
36177     },
36178     
36179     validateValue : function(value)
36180     {
36181         
36182         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36183             return false;
36184         }
36185         
36186         var num = this.parseValue(value);
36187         
36188         if(isNaN(num)){
36189             this.markInvalid(String.format(this.nanText, value));
36190             return false;
36191         }
36192         
36193         if(num < this.minValue){
36194             this.markInvalid(String.format(this.minText, this.minValue));
36195             return false;
36196         }
36197         
36198         if(num > this.maxValue){
36199             this.markInvalid(String.format(this.maxText, this.maxValue));
36200             return false;
36201         }
36202         
36203         return true;
36204     },
36205
36206     getValue : function()
36207     {
36208         var v = this.hiddenEl().getValue();
36209         
36210         return this.fixPrecision(this.parseValue(v));
36211     },
36212
36213     parseValue : function(value)
36214     {
36215         if(this.thousandsDelimiter) {
36216             value += "";
36217             r = new RegExp(",", "g");
36218             value = value.replace(r, "");
36219         }
36220         
36221         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36222         return isNaN(value) ? '' : value;
36223     },
36224
36225     fixPrecision : function(value)
36226     {
36227         if(this.thousandsDelimiter) {
36228             value += "";
36229             r = new RegExp(",", "g");
36230             value = value.replace(r, "");
36231         }
36232         
36233         var nan = isNaN(value);
36234         
36235         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36236             return nan ? '' : value;
36237         }
36238         return parseFloat(value).toFixed(this.decimalPrecision);
36239     },
36240
36241     setValue : function(v)
36242     {
36243         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36244         
36245         this.value = v;
36246         
36247         if(this.rendered){
36248             
36249             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36250             
36251             this.inputEl().dom.value = (v == '') ? '' :
36252                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36253             
36254             if(!this.allowZero && v === '0') {
36255                 this.hiddenEl().dom.value = '';
36256                 this.inputEl().dom.value = '';
36257             }
36258             
36259             this.validate();
36260         }
36261     },
36262
36263     decimalPrecisionFcn : function(v)
36264     {
36265         return Math.floor(v);
36266     },
36267
36268     beforeBlur : function()
36269     {
36270         var v = this.parseValue(this.getRawValue());
36271         
36272         if(v || v === 0 || v === ''){
36273             this.setValue(v);
36274         }
36275     },
36276     
36277     hiddenEl : function()
36278     {
36279         return this.el.select('input.hidden-number-input',true).first();
36280     }
36281     
36282 });
36283
36284  
36285
36286 /*
36287 * Licence: LGPL
36288 */
36289
36290 /**
36291  * @class Roo.bootstrap.DocumentSlider
36292  * @extends Roo.bootstrap.Component
36293  * Bootstrap DocumentSlider class
36294  * 
36295  * @constructor
36296  * Create a new DocumentViewer
36297  * @param {Object} config The config object
36298  */
36299
36300 Roo.bootstrap.DocumentSlider = function(config){
36301     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36302     
36303     this.files = [];
36304     
36305     this.addEvents({
36306         /**
36307          * @event initial
36308          * Fire after initEvent
36309          * @param {Roo.bootstrap.DocumentSlider} this
36310          */
36311         "initial" : true,
36312         /**
36313          * @event update
36314          * Fire after update
36315          * @param {Roo.bootstrap.DocumentSlider} this
36316          */
36317         "update" : true,
36318         /**
36319          * @event click
36320          * Fire after click
36321          * @param {Roo.bootstrap.DocumentSlider} this
36322          */
36323         "click" : true
36324     });
36325 };
36326
36327 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36328     
36329     files : false,
36330     
36331     indicator : 0,
36332     
36333     getAutoCreate : function()
36334     {
36335         var cfg = {
36336             tag : 'div',
36337             cls : 'roo-document-slider',
36338             cn : [
36339                 {
36340                     tag : 'div',
36341                     cls : 'roo-document-slider-header',
36342                     cn : [
36343                         {
36344                             tag : 'div',
36345                             cls : 'roo-document-slider-header-title'
36346                         }
36347                     ]
36348                 },
36349                 {
36350                     tag : 'div',
36351                     cls : 'roo-document-slider-body',
36352                     cn : [
36353                         {
36354                             tag : 'div',
36355                             cls : 'roo-document-slider-prev',
36356                             cn : [
36357                                 {
36358                                     tag : 'i',
36359                                     cls : 'fa fa-chevron-left'
36360                                 }
36361                             ]
36362                         },
36363                         {
36364                             tag : 'div',
36365                             cls : 'roo-document-slider-thumb',
36366                             cn : [
36367                                 {
36368                                     tag : 'img',
36369                                     cls : 'roo-document-slider-image'
36370                                 }
36371                             ]
36372                         },
36373                         {
36374                             tag : 'div',
36375                             cls : 'roo-document-slider-next',
36376                             cn : [
36377                                 {
36378                                     tag : 'i',
36379                                     cls : 'fa fa-chevron-right'
36380                                 }
36381                             ]
36382                         }
36383                     ]
36384                 }
36385             ]
36386         };
36387         
36388         return cfg;
36389     },
36390     
36391     initEvents : function()
36392     {
36393         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36394         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36395         
36396         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36397         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36398         
36399         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36400         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36401         
36402         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36403         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36404         
36405         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36406         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36407         
36408         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36409         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36410         
36411         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36412         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36413         
36414         this.thumbEl.on('click', this.onClick, this);
36415         
36416         this.prevIndicator.on('click', this.prev, this);
36417         
36418         this.nextIndicator.on('click', this.next, this);
36419         
36420     },
36421     
36422     initial : function()
36423     {
36424         if(this.files.length){
36425             this.indicator = 1;
36426             this.update()
36427         }
36428         
36429         this.fireEvent('initial', this);
36430     },
36431     
36432     update : function()
36433     {
36434         this.imageEl.attr('src', this.files[this.indicator - 1]);
36435         
36436         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36437         
36438         this.prevIndicator.show();
36439         
36440         if(this.indicator == 1){
36441             this.prevIndicator.hide();
36442         }
36443         
36444         this.nextIndicator.show();
36445         
36446         if(this.indicator == this.files.length){
36447             this.nextIndicator.hide();
36448         }
36449         
36450         this.thumbEl.scrollTo('top');
36451         
36452         this.fireEvent('update', this);
36453     },
36454     
36455     onClick : function(e)
36456     {
36457         e.preventDefault();
36458         
36459         this.fireEvent('click', this);
36460     },
36461     
36462     prev : function(e)
36463     {
36464         e.preventDefault();
36465         
36466         this.indicator = Math.max(1, this.indicator - 1);
36467         
36468         this.update();
36469     },
36470     
36471     next : function(e)
36472     {
36473         e.preventDefault();
36474         
36475         this.indicator = Math.min(this.files.length, this.indicator + 1);
36476         
36477         this.update();
36478     }
36479 });
36480 /*
36481  * - LGPL
36482  *
36483  * RadioSet
36484  *
36485  *
36486  */
36487
36488 /**
36489  * @class Roo.bootstrap.RadioSet
36490  * @extends Roo.bootstrap.Input
36491  * Bootstrap RadioSet class
36492  * @cfg {String} indicatorpos (left|right) default left
36493  * @cfg {Boolean} inline (true|false) inline the element (default true)
36494  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36495  * @constructor
36496  * Create a new RadioSet
36497  * @param {Object} config The config object
36498  */
36499
36500 Roo.bootstrap.RadioSet = function(config){
36501     
36502     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36503     
36504     this.radioes = [];
36505     
36506     Roo.bootstrap.RadioSet.register(this);
36507     
36508     this.addEvents({
36509         /**
36510         * @event check
36511         * Fires when the element is checked or unchecked.
36512         * @param {Roo.bootstrap.RadioSet} this This radio
36513         * @param {Roo.bootstrap.Radio} item The checked item
36514         */
36515        check : true,
36516        /**
36517         * @event click
36518         * Fires when the element is click.
36519         * @param {Roo.bootstrap.RadioSet} this This radio set
36520         * @param {Roo.bootstrap.Radio} item The checked item
36521         * @param {Roo.EventObject} e The event object
36522         */
36523        click : true
36524     });
36525     
36526 };
36527
36528 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36529
36530     radioes : false,
36531     
36532     inline : true,
36533     
36534     weight : '',
36535     
36536     indicatorpos : 'left',
36537     
36538     getAutoCreate : function()
36539     {
36540         var label = {
36541             tag : 'label',
36542             cls : 'roo-radio-set-label',
36543             cn : [
36544                 {
36545                     tag : 'span',
36546                     html : this.fieldLabel
36547                 }
36548             ]
36549         };
36550         if (Roo.bootstrap.version == 3) {
36551             
36552             
36553             if(this.indicatorpos == 'left'){
36554                 label.cn.unshift({
36555                     tag : 'i',
36556                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36557                     tooltip : 'This field is required'
36558                 });
36559             } else {
36560                 label.cn.push({
36561                     tag : 'i',
36562                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36563                     tooltip : 'This field is required'
36564                 });
36565             }
36566         }
36567         var items = {
36568             tag : 'div',
36569             cls : 'roo-radio-set-items'
36570         };
36571         
36572         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36573         
36574         if (align === 'left' && this.fieldLabel.length) {
36575             
36576             items = {
36577                 cls : "roo-radio-set-right", 
36578                 cn: [
36579                     items
36580                 ]
36581             };
36582             
36583             if(this.labelWidth > 12){
36584                 label.style = "width: " + this.labelWidth + 'px';
36585             }
36586             
36587             if(this.labelWidth < 13 && this.labelmd == 0){
36588                 this.labelmd = this.labelWidth;
36589             }
36590             
36591             if(this.labellg > 0){
36592                 label.cls += ' col-lg-' + this.labellg;
36593                 items.cls += ' col-lg-' + (12 - this.labellg);
36594             }
36595             
36596             if(this.labelmd > 0){
36597                 label.cls += ' col-md-' + this.labelmd;
36598                 items.cls += ' col-md-' + (12 - this.labelmd);
36599             }
36600             
36601             if(this.labelsm > 0){
36602                 label.cls += ' col-sm-' + this.labelsm;
36603                 items.cls += ' col-sm-' + (12 - this.labelsm);
36604             }
36605             
36606             if(this.labelxs > 0){
36607                 label.cls += ' col-xs-' + this.labelxs;
36608                 items.cls += ' col-xs-' + (12 - this.labelxs);
36609             }
36610         }
36611         
36612         var cfg = {
36613             tag : 'div',
36614             cls : 'roo-radio-set',
36615             cn : [
36616                 {
36617                     tag : 'input',
36618                     cls : 'roo-radio-set-input',
36619                     type : 'hidden',
36620                     name : this.name,
36621                     value : this.value ? this.value :  ''
36622                 },
36623                 label,
36624                 items
36625             ]
36626         };
36627         
36628         if(this.weight.length){
36629             cfg.cls += ' roo-radio-' + this.weight;
36630         }
36631         
36632         if(this.inline) {
36633             cfg.cls += ' roo-radio-set-inline';
36634         }
36635         
36636         var settings=this;
36637         ['xs','sm','md','lg'].map(function(size){
36638             if (settings[size]) {
36639                 cfg.cls += ' col-' + size + '-' + settings[size];
36640             }
36641         });
36642         
36643         return cfg;
36644         
36645     },
36646
36647     initEvents : function()
36648     {
36649         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36650         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36651         
36652         if(!this.fieldLabel.length){
36653             this.labelEl.hide();
36654         }
36655         
36656         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36657         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36658         
36659         this.indicator = this.indicatorEl();
36660         
36661         if(this.indicator){
36662             this.indicator.addClass('invisible');
36663         }
36664         
36665         this.originalValue = this.getValue();
36666         
36667     },
36668     
36669     inputEl: function ()
36670     {
36671         return this.el.select('.roo-radio-set-input', true).first();
36672     },
36673     
36674     getChildContainer : function()
36675     {
36676         return this.itemsEl;
36677     },
36678     
36679     register : function(item)
36680     {
36681         this.radioes.push(item);
36682         
36683     },
36684     
36685     validate : function()
36686     {   
36687         if(this.getVisibilityEl().hasClass('hidden')){
36688             return true;
36689         }
36690         
36691         var valid = false;
36692         
36693         Roo.each(this.radioes, function(i){
36694             if(!i.checked){
36695                 return;
36696             }
36697             
36698             valid = true;
36699             return false;
36700         });
36701         
36702         if(this.allowBlank) {
36703             return true;
36704         }
36705         
36706         if(this.disabled || valid){
36707             this.markValid();
36708             return true;
36709         }
36710         
36711         this.markInvalid();
36712         return false;
36713         
36714     },
36715     
36716     markValid : function()
36717     {
36718         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36719             this.indicatorEl().removeClass('visible');
36720             this.indicatorEl().addClass('invisible');
36721         }
36722         
36723         
36724         if (Roo.bootstrap.version == 3) {
36725             this.el.removeClass([this.invalidClass, this.validClass]);
36726             this.el.addClass(this.validClass);
36727         } else {
36728             this.el.removeClass(['is-invalid','is-valid']);
36729             this.el.addClass(['is-valid']);
36730         }
36731         this.fireEvent('valid', this);
36732     },
36733     
36734     markInvalid : function(msg)
36735     {
36736         if(this.allowBlank || this.disabled){
36737             return;
36738         }
36739         
36740         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36741             this.indicatorEl().removeClass('invisible');
36742             this.indicatorEl().addClass('visible');
36743         }
36744         if (Roo.bootstrap.version == 3) {
36745             this.el.removeClass([this.invalidClass, this.validClass]);
36746             this.el.addClass(this.invalidClass);
36747         } else {
36748             this.el.removeClass(['is-invalid','is-valid']);
36749             this.el.addClass(['is-invalid']);
36750         }
36751         
36752         this.fireEvent('invalid', this, msg);
36753         
36754     },
36755     
36756     setValue : function(v, suppressEvent)
36757     {   
36758         if(this.value === v){
36759             return;
36760         }
36761         
36762         this.value = v;
36763         
36764         if(this.rendered){
36765             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36766         }
36767         
36768         Roo.each(this.radioes, function(i){
36769             i.checked = false;
36770             i.el.removeClass('checked');
36771         });
36772         
36773         Roo.each(this.radioes, function(i){
36774             
36775             if(i.value === v || i.value.toString() === v.toString()){
36776                 i.checked = true;
36777                 i.el.addClass('checked');
36778                 
36779                 if(suppressEvent !== true){
36780                     this.fireEvent('check', this, i);
36781                 }
36782                 
36783                 return false;
36784             }
36785             
36786         }, this);
36787         
36788         this.validate();
36789     },
36790     
36791     clearInvalid : function(){
36792         
36793         if(!this.el || this.preventMark){
36794             return;
36795         }
36796         
36797         this.el.removeClass([this.invalidClass]);
36798         
36799         this.fireEvent('valid', this);
36800     }
36801     
36802 });
36803
36804 Roo.apply(Roo.bootstrap.RadioSet, {
36805     
36806     groups: {},
36807     
36808     register : function(set)
36809     {
36810         this.groups[set.name] = set;
36811     },
36812     
36813     get: function(name) 
36814     {
36815         if (typeof(this.groups[name]) == 'undefined') {
36816             return false;
36817         }
36818         
36819         return this.groups[name] ;
36820     }
36821     
36822 });
36823 /*
36824  * Based on:
36825  * Ext JS Library 1.1.1
36826  * Copyright(c) 2006-2007, Ext JS, LLC.
36827  *
36828  * Originally Released Under LGPL - original licence link has changed is not relivant.
36829  *
36830  * Fork - LGPL
36831  * <script type="text/javascript">
36832  */
36833
36834
36835 /**
36836  * @class Roo.bootstrap.SplitBar
36837  * @extends Roo.util.Observable
36838  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36839  * <br><br>
36840  * Usage:
36841  * <pre><code>
36842 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36843                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36844 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36845 split.minSize = 100;
36846 split.maxSize = 600;
36847 split.animate = true;
36848 split.on('moved', splitterMoved);
36849 </code></pre>
36850  * @constructor
36851  * Create a new SplitBar
36852  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36853  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36854  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36855  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36856                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36857                         position of the SplitBar).
36858  */
36859 Roo.bootstrap.SplitBar = function(cfg){
36860     
36861     /** @private */
36862     
36863     //{
36864     //  dragElement : elm
36865     //  resizingElement: el,
36866         // optional..
36867     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36868     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36869         // existingProxy ???
36870     //}
36871     
36872     this.el = Roo.get(cfg.dragElement, true);
36873     this.el.dom.unselectable = "on";
36874     /** @private */
36875     this.resizingEl = Roo.get(cfg.resizingElement, true);
36876
36877     /**
36878      * @private
36879      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36880      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36881      * @type Number
36882      */
36883     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36884     
36885     /**
36886      * The minimum size of the resizing element. (Defaults to 0)
36887      * @type Number
36888      */
36889     this.minSize = 0;
36890     
36891     /**
36892      * The maximum size of the resizing element. (Defaults to 2000)
36893      * @type Number
36894      */
36895     this.maxSize = 2000;
36896     
36897     /**
36898      * Whether to animate the transition to the new size
36899      * @type Boolean
36900      */
36901     this.animate = false;
36902     
36903     /**
36904      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36905      * @type Boolean
36906      */
36907     this.useShim = false;
36908     
36909     /** @private */
36910     this.shim = null;
36911     
36912     if(!cfg.existingProxy){
36913         /** @private */
36914         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36915     }else{
36916         this.proxy = Roo.get(cfg.existingProxy).dom;
36917     }
36918     /** @private */
36919     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36920     
36921     /** @private */
36922     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36923     
36924     /** @private */
36925     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36926     
36927     /** @private */
36928     this.dragSpecs = {};
36929     
36930     /**
36931      * @private The adapter to use to positon and resize elements
36932      */
36933     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36934     this.adapter.init(this);
36935     
36936     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36937         /** @private */
36938         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36939         this.el.addClass("roo-splitbar-h");
36940     }else{
36941         /** @private */
36942         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36943         this.el.addClass("roo-splitbar-v");
36944     }
36945     
36946     this.addEvents({
36947         /**
36948          * @event resize
36949          * Fires when the splitter is moved (alias for {@link #event-moved})
36950          * @param {Roo.bootstrap.SplitBar} this
36951          * @param {Number} newSize the new width or height
36952          */
36953         "resize" : true,
36954         /**
36955          * @event moved
36956          * Fires when the splitter is moved
36957          * @param {Roo.bootstrap.SplitBar} this
36958          * @param {Number} newSize the new width or height
36959          */
36960         "moved" : true,
36961         /**
36962          * @event beforeresize
36963          * Fires before the splitter is dragged
36964          * @param {Roo.bootstrap.SplitBar} this
36965          */
36966         "beforeresize" : true,
36967
36968         "beforeapply" : true
36969     });
36970
36971     Roo.util.Observable.call(this);
36972 };
36973
36974 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36975     onStartProxyDrag : function(x, y){
36976         this.fireEvent("beforeresize", this);
36977         if(!this.overlay){
36978             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36979             o.unselectable();
36980             o.enableDisplayMode("block");
36981             // all splitbars share the same overlay
36982             Roo.bootstrap.SplitBar.prototype.overlay = o;
36983         }
36984         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36985         this.overlay.show();
36986         Roo.get(this.proxy).setDisplayed("block");
36987         var size = this.adapter.getElementSize(this);
36988         this.activeMinSize = this.getMinimumSize();;
36989         this.activeMaxSize = this.getMaximumSize();;
36990         var c1 = size - this.activeMinSize;
36991         var c2 = Math.max(this.activeMaxSize - size, 0);
36992         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36993             this.dd.resetConstraints();
36994             this.dd.setXConstraint(
36995                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36996                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36997             );
36998             this.dd.setYConstraint(0, 0);
36999         }else{
37000             this.dd.resetConstraints();
37001             this.dd.setXConstraint(0, 0);
37002             this.dd.setYConstraint(
37003                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37004                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37005             );
37006          }
37007         this.dragSpecs.startSize = size;
37008         this.dragSpecs.startPoint = [x, y];
37009         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37010     },
37011     
37012     /** 
37013      * @private Called after the drag operation by the DDProxy
37014      */
37015     onEndProxyDrag : function(e){
37016         Roo.get(this.proxy).setDisplayed(false);
37017         var endPoint = Roo.lib.Event.getXY(e);
37018         if(this.overlay){
37019             this.overlay.hide();
37020         }
37021         var newSize;
37022         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37023             newSize = this.dragSpecs.startSize + 
37024                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37025                     endPoint[0] - this.dragSpecs.startPoint[0] :
37026                     this.dragSpecs.startPoint[0] - endPoint[0]
37027                 );
37028         }else{
37029             newSize = this.dragSpecs.startSize + 
37030                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37031                     endPoint[1] - this.dragSpecs.startPoint[1] :
37032                     this.dragSpecs.startPoint[1] - endPoint[1]
37033                 );
37034         }
37035         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37036         if(newSize != this.dragSpecs.startSize){
37037             if(this.fireEvent('beforeapply', this, newSize) !== false){
37038                 this.adapter.setElementSize(this, newSize);
37039                 this.fireEvent("moved", this, newSize);
37040                 this.fireEvent("resize", this, newSize);
37041             }
37042         }
37043     },
37044     
37045     /**
37046      * Get the adapter this SplitBar uses
37047      * @return The adapter object
37048      */
37049     getAdapter : function(){
37050         return this.adapter;
37051     },
37052     
37053     /**
37054      * Set the adapter this SplitBar uses
37055      * @param {Object} adapter A SplitBar adapter object
37056      */
37057     setAdapter : function(adapter){
37058         this.adapter = adapter;
37059         this.adapter.init(this);
37060     },
37061     
37062     /**
37063      * Gets the minimum size for the resizing element
37064      * @return {Number} The minimum size
37065      */
37066     getMinimumSize : function(){
37067         return this.minSize;
37068     },
37069     
37070     /**
37071      * Sets the minimum size for the resizing element
37072      * @param {Number} minSize The minimum size
37073      */
37074     setMinimumSize : function(minSize){
37075         this.minSize = minSize;
37076     },
37077     
37078     /**
37079      * Gets the maximum size for the resizing element
37080      * @return {Number} The maximum size
37081      */
37082     getMaximumSize : function(){
37083         return this.maxSize;
37084     },
37085     
37086     /**
37087      * Sets the maximum size for the resizing element
37088      * @param {Number} maxSize The maximum size
37089      */
37090     setMaximumSize : function(maxSize){
37091         this.maxSize = maxSize;
37092     },
37093     
37094     /**
37095      * Sets the initialize size for the resizing element
37096      * @param {Number} size The initial size
37097      */
37098     setCurrentSize : function(size){
37099         var oldAnimate = this.animate;
37100         this.animate = false;
37101         this.adapter.setElementSize(this, size);
37102         this.animate = oldAnimate;
37103     },
37104     
37105     /**
37106      * Destroy this splitbar. 
37107      * @param {Boolean} removeEl True to remove the element
37108      */
37109     destroy : function(removeEl){
37110         if(this.shim){
37111             this.shim.remove();
37112         }
37113         this.dd.unreg();
37114         this.proxy.parentNode.removeChild(this.proxy);
37115         if(removeEl){
37116             this.el.remove();
37117         }
37118     }
37119 });
37120
37121 /**
37122  * @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.
37123  */
37124 Roo.bootstrap.SplitBar.createProxy = function(dir){
37125     var proxy = new Roo.Element(document.createElement("div"));
37126     proxy.unselectable();
37127     var cls = 'roo-splitbar-proxy';
37128     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37129     document.body.appendChild(proxy.dom);
37130     return proxy.dom;
37131 };
37132
37133 /** 
37134  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37135  * Default Adapter. It assumes the splitter and resizing element are not positioned
37136  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37137  */
37138 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37139 };
37140
37141 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37142     // do nothing for now
37143     init : function(s){
37144     
37145     },
37146     /**
37147      * Called before drag operations to get the current size of the resizing element. 
37148      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37149      */
37150      getElementSize : function(s){
37151         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37152             return s.resizingEl.getWidth();
37153         }else{
37154             return s.resizingEl.getHeight();
37155         }
37156     },
37157     
37158     /**
37159      * Called after drag operations to set the size of the resizing element.
37160      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37161      * @param {Number} newSize The new size to set
37162      * @param {Function} onComplete A function to be invoked when resizing is complete
37163      */
37164     setElementSize : function(s, newSize, onComplete){
37165         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37166             if(!s.animate){
37167                 s.resizingEl.setWidth(newSize);
37168                 if(onComplete){
37169                     onComplete(s, newSize);
37170                 }
37171             }else{
37172                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37173             }
37174         }else{
37175             
37176             if(!s.animate){
37177                 s.resizingEl.setHeight(newSize);
37178                 if(onComplete){
37179                     onComplete(s, newSize);
37180                 }
37181             }else{
37182                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37183             }
37184         }
37185     }
37186 };
37187
37188 /** 
37189  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37190  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37191  * Adapter that  moves the splitter element to align with the resized sizing element. 
37192  * Used with an absolute positioned SplitBar.
37193  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37194  * document.body, make sure you assign an id to the body element.
37195  */
37196 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37197     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37198     this.container = Roo.get(container);
37199 };
37200
37201 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37202     init : function(s){
37203         this.basic.init(s);
37204     },
37205     
37206     getElementSize : function(s){
37207         return this.basic.getElementSize(s);
37208     },
37209     
37210     setElementSize : function(s, newSize, onComplete){
37211         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37212     },
37213     
37214     moveSplitter : function(s){
37215         var yes = Roo.bootstrap.SplitBar;
37216         switch(s.placement){
37217             case yes.LEFT:
37218                 s.el.setX(s.resizingEl.getRight());
37219                 break;
37220             case yes.RIGHT:
37221                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37222                 break;
37223             case yes.TOP:
37224                 s.el.setY(s.resizingEl.getBottom());
37225                 break;
37226             case yes.BOTTOM:
37227                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37228                 break;
37229         }
37230     }
37231 };
37232
37233 /**
37234  * Orientation constant - Create a vertical SplitBar
37235  * @static
37236  * @type Number
37237  */
37238 Roo.bootstrap.SplitBar.VERTICAL = 1;
37239
37240 /**
37241  * Orientation constant - Create a horizontal SplitBar
37242  * @static
37243  * @type Number
37244  */
37245 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37246
37247 /**
37248  * Placement constant - The resizing element is to the left of the splitter element
37249  * @static
37250  * @type Number
37251  */
37252 Roo.bootstrap.SplitBar.LEFT = 1;
37253
37254 /**
37255  * Placement constant - The resizing element is to the right of the splitter element
37256  * @static
37257  * @type Number
37258  */
37259 Roo.bootstrap.SplitBar.RIGHT = 2;
37260
37261 /**
37262  * Placement constant - The resizing element is positioned above the splitter element
37263  * @static
37264  * @type Number
37265  */
37266 Roo.bootstrap.SplitBar.TOP = 3;
37267
37268 /**
37269  * Placement constant - The resizing element is positioned under splitter element
37270  * @static
37271  * @type Number
37272  */
37273 Roo.bootstrap.SplitBar.BOTTOM = 4;
37274 Roo.namespace("Roo.bootstrap.layout");/*
37275  * Based on:
37276  * Ext JS Library 1.1.1
37277  * Copyright(c) 2006-2007, Ext JS, LLC.
37278  *
37279  * Originally Released Under LGPL - original licence link has changed is not relivant.
37280  *
37281  * Fork - LGPL
37282  * <script type="text/javascript">
37283  */
37284
37285 /**
37286  * @class Roo.bootstrap.layout.Manager
37287  * @extends Roo.bootstrap.Component
37288  * Base class for layout managers.
37289  */
37290 Roo.bootstrap.layout.Manager = function(config)
37291 {
37292     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37293
37294
37295
37296
37297
37298     /** false to disable window resize monitoring @type Boolean */
37299     this.monitorWindowResize = true;
37300     this.regions = {};
37301     this.addEvents({
37302         /**
37303          * @event layout
37304          * Fires when a layout is performed.
37305          * @param {Roo.LayoutManager} this
37306          */
37307         "layout" : true,
37308         /**
37309          * @event regionresized
37310          * Fires when the user resizes a region.
37311          * @param {Roo.LayoutRegion} region The resized region
37312          * @param {Number} newSize The new size (width for east/west, height for north/south)
37313          */
37314         "regionresized" : true,
37315         /**
37316          * @event regioncollapsed
37317          * Fires when a region is collapsed.
37318          * @param {Roo.LayoutRegion} region The collapsed region
37319          */
37320         "regioncollapsed" : true,
37321         /**
37322          * @event regionexpanded
37323          * Fires when a region is expanded.
37324          * @param {Roo.LayoutRegion} region The expanded region
37325          */
37326         "regionexpanded" : true
37327     });
37328     this.updating = false;
37329
37330     if (config.el) {
37331         this.el = Roo.get(config.el);
37332         this.initEvents();
37333     }
37334
37335 };
37336
37337 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37338
37339
37340     regions : null,
37341
37342     monitorWindowResize : true,
37343
37344
37345     updating : false,
37346
37347
37348     onRender : function(ct, position)
37349     {
37350         if(!this.el){
37351             this.el = Roo.get(ct);
37352             this.initEvents();
37353         }
37354         //this.fireEvent('render',this);
37355     },
37356
37357
37358     initEvents: function()
37359     {
37360
37361
37362         // ie scrollbar fix
37363         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37364             document.body.scroll = "no";
37365         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37366             this.el.position('relative');
37367         }
37368         this.id = this.el.id;
37369         this.el.addClass("roo-layout-container");
37370         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37371         if(this.el.dom != document.body ) {
37372             this.el.on('resize', this.layout,this);
37373             this.el.on('show', this.layout,this);
37374         }
37375
37376     },
37377
37378     /**
37379      * Returns true if this layout is currently being updated
37380      * @return {Boolean}
37381      */
37382     isUpdating : function(){
37383         return this.updating;
37384     },
37385
37386     /**
37387      * Suspend the LayoutManager from doing auto-layouts while
37388      * making multiple add or remove calls
37389      */
37390     beginUpdate : function(){
37391         this.updating = true;
37392     },
37393
37394     /**
37395      * Restore auto-layouts and optionally disable the manager from performing a layout
37396      * @param {Boolean} noLayout true to disable a layout update
37397      */
37398     endUpdate : function(noLayout){
37399         this.updating = false;
37400         if(!noLayout){
37401             this.layout();
37402         }
37403     },
37404
37405     layout: function(){
37406         // abstract...
37407     },
37408
37409     onRegionResized : function(region, newSize){
37410         this.fireEvent("regionresized", region, newSize);
37411         this.layout();
37412     },
37413
37414     onRegionCollapsed : function(region){
37415         this.fireEvent("regioncollapsed", region);
37416     },
37417
37418     onRegionExpanded : function(region){
37419         this.fireEvent("regionexpanded", region);
37420     },
37421
37422     /**
37423      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37424      * performs box-model adjustments.
37425      * @return {Object} The size as an object {width: (the width), height: (the height)}
37426      */
37427     getViewSize : function()
37428     {
37429         var size;
37430         if(this.el.dom != document.body){
37431             size = this.el.getSize();
37432         }else{
37433             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37434         }
37435         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37436         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37437         return size;
37438     },
37439
37440     /**
37441      * Returns the Element this layout is bound to.
37442      * @return {Roo.Element}
37443      */
37444     getEl : function(){
37445         return this.el;
37446     },
37447
37448     /**
37449      * Returns the specified region.
37450      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37451      * @return {Roo.LayoutRegion}
37452      */
37453     getRegion : function(target){
37454         return this.regions[target.toLowerCase()];
37455     },
37456
37457     onWindowResize : function(){
37458         if(this.monitorWindowResize){
37459             this.layout();
37460         }
37461     }
37462 });
37463 /*
37464  * Based on:
37465  * Ext JS Library 1.1.1
37466  * Copyright(c) 2006-2007, Ext JS, LLC.
37467  *
37468  * Originally Released Under LGPL - original licence link has changed is not relivant.
37469  *
37470  * Fork - LGPL
37471  * <script type="text/javascript">
37472  */
37473 /**
37474  * @class Roo.bootstrap.layout.Border
37475  * @extends Roo.bootstrap.layout.Manager
37476  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37477  * please see: examples/bootstrap/nested.html<br><br>
37478  
37479 <b>The container the layout is rendered into can be either the body element or any other element.
37480 If it is not the body element, the container needs to either be an absolute positioned element,
37481 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37482 the container size if it is not the body element.</b>
37483
37484 * @constructor
37485 * Create a new Border
37486 * @param {Object} config Configuration options
37487  */
37488 Roo.bootstrap.layout.Border = function(config){
37489     config = config || {};
37490     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37491     
37492     
37493     
37494     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37495         if(config[region]){
37496             config[region].region = region;
37497             this.addRegion(config[region]);
37498         }
37499     },this);
37500     
37501 };
37502
37503 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37504
37505 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37506     
37507     parent : false, // this might point to a 'nest' or a ???
37508     
37509     /**
37510      * Creates and adds a new region if it doesn't already exist.
37511      * @param {String} target The target region key (north, south, east, west or center).
37512      * @param {Object} config The regions config object
37513      * @return {BorderLayoutRegion} The new region
37514      */
37515     addRegion : function(config)
37516     {
37517         if(!this.regions[config.region]){
37518             var r = this.factory(config);
37519             this.bindRegion(r);
37520         }
37521         return this.regions[config.region];
37522     },
37523
37524     // private (kinda)
37525     bindRegion : function(r){
37526         this.regions[r.config.region] = r;
37527         
37528         r.on("visibilitychange",    this.layout, this);
37529         r.on("paneladded",          this.layout, this);
37530         r.on("panelremoved",        this.layout, this);
37531         r.on("invalidated",         this.layout, this);
37532         r.on("resized",             this.onRegionResized, this);
37533         r.on("collapsed",           this.onRegionCollapsed, this);
37534         r.on("expanded",            this.onRegionExpanded, this);
37535     },
37536
37537     /**
37538      * Performs a layout update.
37539      */
37540     layout : function()
37541     {
37542         if(this.updating) {
37543             return;
37544         }
37545         
37546         // render all the rebions if they have not been done alreayd?
37547         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37548             if(this.regions[region] && !this.regions[region].bodyEl){
37549                 this.regions[region].onRender(this.el)
37550             }
37551         },this);
37552         
37553         var size = this.getViewSize();
37554         var w = size.width;
37555         var h = size.height;
37556         var centerW = w;
37557         var centerH = h;
37558         var centerY = 0;
37559         var centerX = 0;
37560         //var x = 0, y = 0;
37561
37562         var rs = this.regions;
37563         var north = rs["north"];
37564         var south = rs["south"]; 
37565         var west = rs["west"];
37566         var east = rs["east"];
37567         var center = rs["center"];
37568         //if(this.hideOnLayout){ // not supported anymore
37569             //c.el.setStyle("display", "none");
37570         //}
37571         if(north && north.isVisible()){
37572             var b = north.getBox();
37573             var m = north.getMargins();
37574             b.width = w - (m.left+m.right);
37575             b.x = m.left;
37576             b.y = m.top;
37577             centerY = b.height + b.y + m.bottom;
37578             centerH -= centerY;
37579             north.updateBox(this.safeBox(b));
37580         }
37581         if(south && south.isVisible()){
37582             var b = south.getBox();
37583             var m = south.getMargins();
37584             b.width = w - (m.left+m.right);
37585             b.x = m.left;
37586             var totalHeight = (b.height + m.top + m.bottom);
37587             b.y = h - totalHeight + m.top;
37588             centerH -= totalHeight;
37589             south.updateBox(this.safeBox(b));
37590         }
37591         if(west && west.isVisible()){
37592             var b = west.getBox();
37593             var m = west.getMargins();
37594             b.height = centerH - (m.top+m.bottom);
37595             b.x = m.left;
37596             b.y = centerY + m.top;
37597             var totalWidth = (b.width + m.left + m.right);
37598             centerX += totalWidth;
37599             centerW -= totalWidth;
37600             west.updateBox(this.safeBox(b));
37601         }
37602         if(east && east.isVisible()){
37603             var b = east.getBox();
37604             var m = east.getMargins();
37605             b.height = centerH - (m.top+m.bottom);
37606             var totalWidth = (b.width + m.left + m.right);
37607             b.x = w - totalWidth + m.left;
37608             b.y = centerY + m.top;
37609             centerW -= totalWidth;
37610             east.updateBox(this.safeBox(b));
37611         }
37612         if(center){
37613             var m = center.getMargins();
37614             var centerBox = {
37615                 x: centerX + m.left,
37616                 y: centerY + m.top,
37617                 width: centerW - (m.left+m.right),
37618                 height: centerH - (m.top+m.bottom)
37619             };
37620             //if(this.hideOnLayout){
37621                 //center.el.setStyle("display", "block");
37622             //}
37623             center.updateBox(this.safeBox(centerBox));
37624         }
37625         this.el.repaint();
37626         this.fireEvent("layout", this);
37627     },
37628
37629     // private
37630     safeBox : function(box){
37631         box.width = Math.max(0, box.width);
37632         box.height = Math.max(0, box.height);
37633         return box;
37634     },
37635
37636     /**
37637      * Adds a ContentPanel (or subclass) to this layout.
37638      * @param {String} target The target region key (north, south, east, west or center).
37639      * @param {Roo.ContentPanel} panel The panel to add
37640      * @return {Roo.ContentPanel} The added panel
37641      */
37642     add : function(target, panel){
37643          
37644         target = target.toLowerCase();
37645         return this.regions[target].add(panel);
37646     },
37647
37648     /**
37649      * Remove a ContentPanel (or subclass) to this layout.
37650      * @param {String} target The target region key (north, south, east, west or center).
37651      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37652      * @return {Roo.ContentPanel} The removed panel
37653      */
37654     remove : function(target, panel){
37655         target = target.toLowerCase();
37656         return this.regions[target].remove(panel);
37657     },
37658
37659     /**
37660      * Searches all regions for a panel with the specified id
37661      * @param {String} panelId
37662      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37663      */
37664     findPanel : function(panelId){
37665         var rs = this.regions;
37666         for(var target in rs){
37667             if(typeof rs[target] != "function"){
37668                 var p = rs[target].getPanel(panelId);
37669                 if(p){
37670                     return p;
37671                 }
37672             }
37673         }
37674         return null;
37675     },
37676
37677     /**
37678      * Searches all regions for a panel with the specified id and activates (shows) it.
37679      * @param {String/ContentPanel} panelId The panels id or the panel itself
37680      * @return {Roo.ContentPanel} The shown panel or null
37681      */
37682     showPanel : function(panelId) {
37683       var rs = this.regions;
37684       for(var target in rs){
37685          var r = rs[target];
37686          if(typeof r != "function"){
37687             if(r.hasPanel(panelId)){
37688                return r.showPanel(panelId);
37689             }
37690          }
37691       }
37692       return null;
37693    },
37694
37695    /**
37696      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37697      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37698      */
37699    /*
37700     restoreState : function(provider){
37701         if(!provider){
37702             provider = Roo.state.Manager;
37703         }
37704         var sm = new Roo.LayoutStateManager();
37705         sm.init(this, provider);
37706     },
37707 */
37708  
37709  
37710     /**
37711      * Adds a xtype elements to the layout.
37712      * <pre><code>
37713
37714 layout.addxtype({
37715        xtype : 'ContentPanel',
37716        region: 'west',
37717        items: [ .... ]
37718    }
37719 );
37720
37721 layout.addxtype({
37722         xtype : 'NestedLayoutPanel',
37723         region: 'west',
37724         layout: {
37725            center: { },
37726            west: { }   
37727         },
37728         items : [ ... list of content panels or nested layout panels.. ]
37729    }
37730 );
37731 </code></pre>
37732      * @param {Object} cfg Xtype definition of item to add.
37733      */
37734     addxtype : function(cfg)
37735     {
37736         // basically accepts a pannel...
37737         // can accept a layout region..!?!?
37738         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37739         
37740         
37741         // theory?  children can only be panels??
37742         
37743         //if (!cfg.xtype.match(/Panel$/)) {
37744         //    return false;
37745         //}
37746         var ret = false;
37747         
37748         if (typeof(cfg.region) == 'undefined') {
37749             Roo.log("Failed to add Panel, region was not set");
37750             Roo.log(cfg);
37751             return false;
37752         }
37753         var region = cfg.region;
37754         delete cfg.region;
37755         
37756           
37757         var xitems = [];
37758         if (cfg.items) {
37759             xitems = cfg.items;
37760             delete cfg.items;
37761         }
37762         var nb = false;
37763         
37764         if ( region == 'center') {
37765             Roo.log("Center: " + cfg.title);
37766         }
37767         
37768         
37769         switch(cfg.xtype) 
37770         {
37771             case 'Content':  // ContentPanel (el, cfg)
37772             case 'Scroll':  // ContentPanel (el, cfg)
37773             case 'View': 
37774                 cfg.autoCreate = cfg.autoCreate || true;
37775                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37776                 //} else {
37777                 //    var el = this.el.createChild();
37778                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37779                 //}
37780                 
37781                 this.add(region, ret);
37782                 break;
37783             
37784             /*
37785             case 'TreePanel': // our new panel!
37786                 cfg.el = this.el.createChild();
37787                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37788                 this.add(region, ret);
37789                 break;
37790             */
37791             
37792             case 'Nest': 
37793                 // create a new Layout (which is  a Border Layout...
37794                 
37795                 var clayout = cfg.layout;
37796                 clayout.el  = this.el.createChild();
37797                 clayout.items   = clayout.items  || [];
37798                 
37799                 delete cfg.layout;
37800                 
37801                 // replace this exitems with the clayout ones..
37802                 xitems = clayout.items;
37803                  
37804                 // force background off if it's in center...
37805                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37806                     cfg.background = false;
37807                 }
37808                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37809                 
37810                 
37811                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37812                 //console.log('adding nested layout panel '  + cfg.toSource());
37813                 this.add(region, ret);
37814                 nb = {}; /// find first...
37815                 break;
37816             
37817             case 'Grid':
37818                 
37819                 // needs grid and region
37820                 
37821                 //var el = this.getRegion(region).el.createChild();
37822                 /*
37823                  *var el = this.el.createChild();
37824                 // create the grid first...
37825                 cfg.grid.container = el;
37826                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37827                 */
37828                 
37829                 if (region == 'center' && this.active ) {
37830                     cfg.background = false;
37831                 }
37832                 
37833                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37834                 
37835                 this.add(region, ret);
37836                 /*
37837                 if (cfg.background) {
37838                     // render grid on panel activation (if panel background)
37839                     ret.on('activate', function(gp) {
37840                         if (!gp.grid.rendered) {
37841                     //        gp.grid.render(el);
37842                         }
37843                     });
37844                 } else {
37845                   //  cfg.grid.render(el);
37846                 }
37847                 */
37848                 break;
37849            
37850            
37851             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37852                 // it was the old xcomponent building that caused this before.
37853                 // espeically if border is the top element in the tree.
37854                 ret = this;
37855                 break; 
37856                 
37857                     
37858                 
37859                 
37860                 
37861             default:
37862                 /*
37863                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37864                     
37865                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37866                     this.add(region, ret);
37867                 } else {
37868                 */
37869                     Roo.log(cfg);
37870                     throw "Can not add '" + cfg.xtype + "' to Border";
37871                     return null;
37872              
37873                                 
37874              
37875         }
37876         this.beginUpdate();
37877         // add children..
37878         var region = '';
37879         var abn = {};
37880         Roo.each(xitems, function(i)  {
37881             region = nb && i.region ? i.region : false;
37882             
37883             var add = ret.addxtype(i);
37884            
37885             if (region) {
37886                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37887                 if (!i.background) {
37888                     abn[region] = nb[region] ;
37889                 }
37890             }
37891             
37892         });
37893         this.endUpdate();
37894
37895         // make the last non-background panel active..
37896         //if (nb) { Roo.log(abn); }
37897         if (nb) {
37898             
37899             for(var r in abn) {
37900                 region = this.getRegion(r);
37901                 if (region) {
37902                     // tried using nb[r], but it does not work..
37903                      
37904                     region.showPanel(abn[r]);
37905                    
37906                 }
37907             }
37908         }
37909         return ret;
37910         
37911     },
37912     
37913     
37914 // private
37915     factory : function(cfg)
37916     {
37917         
37918         var validRegions = Roo.bootstrap.layout.Border.regions;
37919
37920         var target = cfg.region;
37921         cfg.mgr = this;
37922         
37923         var r = Roo.bootstrap.layout;
37924         Roo.log(target);
37925         switch(target){
37926             case "north":
37927                 return new r.North(cfg);
37928             case "south":
37929                 return new r.South(cfg);
37930             case "east":
37931                 return new r.East(cfg);
37932             case "west":
37933                 return new r.West(cfg);
37934             case "center":
37935                 return new r.Center(cfg);
37936         }
37937         throw 'Layout region "'+target+'" not supported.';
37938     }
37939     
37940     
37941 });
37942  /*
37943  * Based on:
37944  * Ext JS Library 1.1.1
37945  * Copyright(c) 2006-2007, Ext JS, LLC.
37946  *
37947  * Originally Released Under LGPL - original licence link has changed is not relivant.
37948  *
37949  * Fork - LGPL
37950  * <script type="text/javascript">
37951  */
37952  
37953 /**
37954  * @class Roo.bootstrap.layout.Basic
37955  * @extends Roo.util.Observable
37956  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37957  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37958  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37959  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37960  * @cfg {string}   region  the region that it inhabits..
37961  * @cfg {bool}   skipConfig skip config?
37962  * 
37963
37964  */
37965 Roo.bootstrap.layout.Basic = function(config){
37966     
37967     this.mgr = config.mgr;
37968     
37969     this.position = config.region;
37970     
37971     var skipConfig = config.skipConfig;
37972     
37973     this.events = {
37974         /**
37975          * @scope Roo.BasicLayoutRegion
37976          */
37977         
37978         /**
37979          * @event beforeremove
37980          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37981          * @param {Roo.LayoutRegion} this
37982          * @param {Roo.ContentPanel} panel The panel
37983          * @param {Object} e The cancel event object
37984          */
37985         "beforeremove" : true,
37986         /**
37987          * @event invalidated
37988          * Fires when the layout for this region is changed.
37989          * @param {Roo.LayoutRegion} this
37990          */
37991         "invalidated" : true,
37992         /**
37993          * @event visibilitychange
37994          * Fires when this region is shown or hidden 
37995          * @param {Roo.LayoutRegion} this
37996          * @param {Boolean} visibility true or false
37997          */
37998         "visibilitychange" : true,
37999         /**
38000          * @event paneladded
38001          * Fires when a panel is added. 
38002          * @param {Roo.LayoutRegion} this
38003          * @param {Roo.ContentPanel} panel The panel
38004          */
38005         "paneladded" : true,
38006         /**
38007          * @event panelremoved
38008          * Fires when a panel is removed. 
38009          * @param {Roo.LayoutRegion} this
38010          * @param {Roo.ContentPanel} panel The panel
38011          */
38012         "panelremoved" : true,
38013         /**
38014          * @event beforecollapse
38015          * Fires when this region before collapse.
38016          * @param {Roo.LayoutRegion} this
38017          */
38018         "beforecollapse" : true,
38019         /**
38020          * @event collapsed
38021          * Fires when this region is collapsed.
38022          * @param {Roo.LayoutRegion} this
38023          */
38024         "collapsed" : true,
38025         /**
38026          * @event expanded
38027          * Fires when this region is expanded.
38028          * @param {Roo.LayoutRegion} this
38029          */
38030         "expanded" : true,
38031         /**
38032          * @event slideshow
38033          * Fires when this region is slid into view.
38034          * @param {Roo.LayoutRegion} this
38035          */
38036         "slideshow" : true,
38037         /**
38038          * @event slidehide
38039          * Fires when this region slides out of view. 
38040          * @param {Roo.LayoutRegion} this
38041          */
38042         "slidehide" : true,
38043         /**
38044          * @event panelactivated
38045          * Fires when a panel is activated. 
38046          * @param {Roo.LayoutRegion} this
38047          * @param {Roo.ContentPanel} panel The activated panel
38048          */
38049         "panelactivated" : true,
38050         /**
38051          * @event resized
38052          * Fires when the user resizes this region. 
38053          * @param {Roo.LayoutRegion} this
38054          * @param {Number} newSize The new size (width for east/west, height for north/south)
38055          */
38056         "resized" : true
38057     };
38058     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38059     this.panels = new Roo.util.MixedCollection();
38060     this.panels.getKey = this.getPanelId.createDelegate(this);
38061     this.box = null;
38062     this.activePanel = null;
38063     // ensure listeners are added...
38064     
38065     if (config.listeners || config.events) {
38066         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38067             listeners : config.listeners || {},
38068             events : config.events || {}
38069         });
38070     }
38071     
38072     if(skipConfig !== true){
38073         this.applyConfig(config);
38074     }
38075 };
38076
38077 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38078 {
38079     getPanelId : function(p){
38080         return p.getId();
38081     },
38082     
38083     applyConfig : function(config){
38084         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38085         this.config = config;
38086         
38087     },
38088     
38089     /**
38090      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38091      * the width, for horizontal (north, south) the height.
38092      * @param {Number} newSize The new width or height
38093      */
38094     resizeTo : function(newSize){
38095         var el = this.el ? this.el :
38096                  (this.activePanel ? this.activePanel.getEl() : null);
38097         if(el){
38098             switch(this.position){
38099                 case "east":
38100                 case "west":
38101                     el.setWidth(newSize);
38102                     this.fireEvent("resized", this, newSize);
38103                 break;
38104                 case "north":
38105                 case "south":
38106                     el.setHeight(newSize);
38107                     this.fireEvent("resized", this, newSize);
38108                 break;                
38109             }
38110         }
38111     },
38112     
38113     getBox : function(){
38114         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38115     },
38116     
38117     getMargins : function(){
38118         return this.margins;
38119     },
38120     
38121     updateBox : function(box){
38122         this.box = box;
38123         var el = this.activePanel.getEl();
38124         el.dom.style.left = box.x + "px";
38125         el.dom.style.top = box.y + "px";
38126         this.activePanel.setSize(box.width, box.height);
38127     },
38128     
38129     /**
38130      * Returns the container element for this region.
38131      * @return {Roo.Element}
38132      */
38133     getEl : function(){
38134         return this.activePanel;
38135     },
38136     
38137     /**
38138      * Returns true if this region is currently visible.
38139      * @return {Boolean}
38140      */
38141     isVisible : function(){
38142         return this.activePanel ? true : false;
38143     },
38144     
38145     setActivePanel : function(panel){
38146         panel = this.getPanel(panel);
38147         if(this.activePanel && this.activePanel != panel){
38148             this.activePanel.setActiveState(false);
38149             this.activePanel.getEl().setLeftTop(-10000,-10000);
38150         }
38151         this.activePanel = panel;
38152         panel.setActiveState(true);
38153         if(this.box){
38154             panel.setSize(this.box.width, this.box.height);
38155         }
38156         this.fireEvent("panelactivated", this, panel);
38157         this.fireEvent("invalidated");
38158     },
38159     
38160     /**
38161      * Show the specified panel.
38162      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38163      * @return {Roo.ContentPanel} The shown panel or null
38164      */
38165     showPanel : function(panel){
38166         panel = this.getPanel(panel);
38167         if(panel){
38168             this.setActivePanel(panel);
38169         }
38170         return panel;
38171     },
38172     
38173     /**
38174      * Get the active panel for this region.
38175      * @return {Roo.ContentPanel} The active panel or null
38176      */
38177     getActivePanel : function(){
38178         return this.activePanel;
38179     },
38180     
38181     /**
38182      * Add the passed ContentPanel(s)
38183      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38184      * @return {Roo.ContentPanel} The panel added (if only one was added)
38185      */
38186     add : function(panel){
38187         if(arguments.length > 1){
38188             for(var i = 0, len = arguments.length; i < len; i++) {
38189                 this.add(arguments[i]);
38190             }
38191             return null;
38192         }
38193         if(this.hasPanel(panel)){
38194             this.showPanel(panel);
38195             return panel;
38196         }
38197         var el = panel.getEl();
38198         if(el.dom.parentNode != this.mgr.el.dom){
38199             this.mgr.el.dom.appendChild(el.dom);
38200         }
38201         if(panel.setRegion){
38202             panel.setRegion(this);
38203         }
38204         this.panels.add(panel);
38205         el.setStyle("position", "absolute");
38206         if(!panel.background){
38207             this.setActivePanel(panel);
38208             if(this.config.initialSize && this.panels.getCount()==1){
38209                 this.resizeTo(this.config.initialSize);
38210             }
38211         }
38212         this.fireEvent("paneladded", this, panel);
38213         return panel;
38214     },
38215     
38216     /**
38217      * Returns true if the panel is in this region.
38218      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38219      * @return {Boolean}
38220      */
38221     hasPanel : function(panel){
38222         if(typeof panel == "object"){ // must be panel obj
38223             panel = panel.getId();
38224         }
38225         return this.getPanel(panel) ? true : false;
38226     },
38227     
38228     /**
38229      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38230      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38231      * @param {Boolean} preservePanel Overrides the config preservePanel option
38232      * @return {Roo.ContentPanel} The panel that was removed
38233      */
38234     remove : function(panel, preservePanel){
38235         panel = this.getPanel(panel);
38236         if(!panel){
38237             return null;
38238         }
38239         var e = {};
38240         this.fireEvent("beforeremove", this, panel, e);
38241         if(e.cancel === true){
38242             return null;
38243         }
38244         var panelId = panel.getId();
38245         this.panels.removeKey(panelId);
38246         return panel;
38247     },
38248     
38249     /**
38250      * Returns the panel specified or null if it's not in this region.
38251      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38252      * @return {Roo.ContentPanel}
38253      */
38254     getPanel : function(id){
38255         if(typeof id == "object"){ // must be panel obj
38256             return id;
38257         }
38258         return this.panels.get(id);
38259     },
38260     
38261     /**
38262      * Returns this regions position (north/south/east/west/center).
38263      * @return {String} 
38264      */
38265     getPosition: function(){
38266         return this.position;    
38267     }
38268 });/*
38269  * Based on:
38270  * Ext JS Library 1.1.1
38271  * Copyright(c) 2006-2007, Ext JS, LLC.
38272  *
38273  * Originally Released Under LGPL - original licence link has changed is not relivant.
38274  *
38275  * Fork - LGPL
38276  * <script type="text/javascript">
38277  */
38278  
38279 /**
38280  * @class Roo.bootstrap.layout.Region
38281  * @extends Roo.bootstrap.layout.Basic
38282  * This class represents a region in a layout manager.
38283  
38284  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38285  * @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})
38286  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38287  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38288  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38289  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38290  * @cfg {String}    title           The title for the region (overrides panel titles)
38291  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38292  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38293  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38294  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38295  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38296  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38297  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38298  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38299  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38300  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38301
38302  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38303  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38304  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38305  * @cfg {Number}    width           For East/West panels
38306  * @cfg {Number}    height          For North/South panels
38307  * @cfg {Boolean}   split           To show the splitter
38308  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38309  * 
38310  * @cfg {string}   cls             Extra CSS classes to add to region
38311  * 
38312  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38313  * @cfg {string}   region  the region that it inhabits..
38314  *
38315
38316  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38317  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38318
38319  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38320  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38321  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38322  */
38323 Roo.bootstrap.layout.Region = function(config)
38324 {
38325     this.applyConfig(config);
38326
38327     var mgr = config.mgr;
38328     var pos = config.region;
38329     config.skipConfig = true;
38330     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38331     
38332     if (mgr.el) {
38333         this.onRender(mgr.el);   
38334     }
38335      
38336     this.visible = true;
38337     this.collapsed = false;
38338     this.unrendered_panels = [];
38339 };
38340
38341 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38342
38343     position: '', // set by wrapper (eg. north/south etc..)
38344     unrendered_panels : null,  // unrendered panels.
38345     
38346     tabPosition : false,
38347     
38348     mgr: false, // points to 'Border'
38349     
38350     
38351     createBody : function(){
38352         /** This region's body element 
38353         * @type Roo.Element */
38354         this.bodyEl = this.el.createChild({
38355                 tag: "div",
38356                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38357         });
38358     },
38359
38360     onRender: function(ctr, pos)
38361     {
38362         var dh = Roo.DomHelper;
38363         /** This region's container element 
38364         * @type Roo.Element */
38365         this.el = dh.append(ctr.dom, {
38366                 tag: "div",
38367                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38368             }, true);
38369         /** This region's title element 
38370         * @type Roo.Element */
38371     
38372         this.titleEl = dh.append(this.el.dom,  {
38373                 tag: "div",
38374                 unselectable: "on",
38375                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38376                 children:[
38377                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38378                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38379                 ]
38380             }, true);
38381         
38382         this.titleEl.enableDisplayMode();
38383         /** This region's title text element 
38384         * @type HTMLElement */
38385         this.titleTextEl = this.titleEl.dom.firstChild;
38386         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38387         /*
38388         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38389         this.closeBtn.enableDisplayMode();
38390         this.closeBtn.on("click", this.closeClicked, this);
38391         this.closeBtn.hide();
38392     */
38393         this.createBody(this.config);
38394         if(this.config.hideWhenEmpty){
38395             this.hide();
38396             this.on("paneladded", this.validateVisibility, this);
38397             this.on("panelremoved", this.validateVisibility, this);
38398         }
38399         if(this.autoScroll){
38400             this.bodyEl.setStyle("overflow", "auto");
38401         }else{
38402             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38403         }
38404         //if(c.titlebar !== false){
38405             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38406                 this.titleEl.hide();
38407             }else{
38408                 this.titleEl.show();
38409                 if(this.config.title){
38410                     this.titleTextEl.innerHTML = this.config.title;
38411                 }
38412             }
38413         //}
38414         if(this.config.collapsed){
38415             this.collapse(true);
38416         }
38417         if(this.config.hidden){
38418             this.hide();
38419         }
38420         
38421         if (this.unrendered_panels && this.unrendered_panels.length) {
38422             for (var i =0;i< this.unrendered_panels.length; i++) {
38423                 this.add(this.unrendered_panels[i]);
38424             }
38425             this.unrendered_panels = null;
38426             
38427         }
38428         
38429     },
38430     
38431     applyConfig : function(c)
38432     {
38433         /*
38434          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38435             var dh = Roo.DomHelper;
38436             if(c.titlebar !== false){
38437                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38438                 this.collapseBtn.on("click", this.collapse, this);
38439                 this.collapseBtn.enableDisplayMode();
38440                 /*
38441                 if(c.showPin === true || this.showPin){
38442                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38443                     this.stickBtn.enableDisplayMode();
38444                     this.stickBtn.on("click", this.expand, this);
38445                     this.stickBtn.hide();
38446                 }
38447                 
38448             }
38449             */
38450             /** This region's collapsed element
38451             * @type Roo.Element */
38452             /*
38453              *
38454             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38455                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38456             ]}, true);
38457             
38458             if(c.floatable !== false){
38459                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38460                this.collapsedEl.on("click", this.collapseClick, this);
38461             }
38462
38463             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38464                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38465                    id: "message", unselectable: "on", style:{"float":"left"}});
38466                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38467              }
38468             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38469             this.expandBtn.on("click", this.expand, this);
38470             
38471         }
38472         
38473         if(this.collapseBtn){
38474             this.collapseBtn.setVisible(c.collapsible == true);
38475         }
38476         
38477         this.cmargins = c.cmargins || this.cmargins ||
38478                          (this.position == "west" || this.position == "east" ?
38479                              {top: 0, left: 2, right:2, bottom: 0} :
38480                              {top: 2, left: 0, right:0, bottom: 2});
38481         */
38482         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38483         
38484         
38485         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38486         
38487         this.autoScroll = c.autoScroll || false;
38488         
38489         
38490        
38491         
38492         this.duration = c.duration || .30;
38493         this.slideDuration = c.slideDuration || .45;
38494         this.config = c;
38495        
38496     },
38497     /**
38498      * Returns true if this region is currently visible.
38499      * @return {Boolean}
38500      */
38501     isVisible : function(){
38502         return this.visible;
38503     },
38504
38505     /**
38506      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38507      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38508      */
38509     //setCollapsedTitle : function(title){
38510     //    title = title || "&#160;";
38511      //   if(this.collapsedTitleTextEl){
38512       //      this.collapsedTitleTextEl.innerHTML = title;
38513        // }
38514     //},
38515
38516     getBox : function(){
38517         var b;
38518       //  if(!this.collapsed){
38519             b = this.el.getBox(false, true);
38520        // }else{
38521           //  b = this.collapsedEl.getBox(false, true);
38522         //}
38523         return b;
38524     },
38525
38526     getMargins : function(){
38527         return this.margins;
38528         //return this.collapsed ? this.cmargins : this.margins;
38529     },
38530 /*
38531     highlight : function(){
38532         this.el.addClass("x-layout-panel-dragover");
38533     },
38534
38535     unhighlight : function(){
38536         this.el.removeClass("x-layout-panel-dragover");
38537     },
38538 */
38539     updateBox : function(box)
38540     {
38541         if (!this.bodyEl) {
38542             return; // not rendered yet..
38543         }
38544         
38545         this.box = box;
38546         if(!this.collapsed){
38547             this.el.dom.style.left = box.x + "px";
38548             this.el.dom.style.top = box.y + "px";
38549             this.updateBody(box.width, box.height);
38550         }else{
38551             this.collapsedEl.dom.style.left = box.x + "px";
38552             this.collapsedEl.dom.style.top = box.y + "px";
38553             this.collapsedEl.setSize(box.width, box.height);
38554         }
38555         if(this.tabs){
38556             this.tabs.autoSizeTabs();
38557         }
38558     },
38559
38560     updateBody : function(w, h)
38561     {
38562         if(w !== null){
38563             this.el.setWidth(w);
38564             w -= this.el.getBorderWidth("rl");
38565             if(this.config.adjustments){
38566                 w += this.config.adjustments[0];
38567             }
38568         }
38569         if(h !== null && h > 0){
38570             this.el.setHeight(h);
38571             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38572             h -= this.el.getBorderWidth("tb");
38573             if(this.config.adjustments){
38574                 h += this.config.adjustments[1];
38575             }
38576             this.bodyEl.setHeight(h);
38577             if(this.tabs){
38578                 h = this.tabs.syncHeight(h);
38579             }
38580         }
38581         if(this.panelSize){
38582             w = w !== null ? w : this.panelSize.width;
38583             h = h !== null ? h : this.panelSize.height;
38584         }
38585         if(this.activePanel){
38586             var el = this.activePanel.getEl();
38587             w = w !== null ? w : el.getWidth();
38588             h = h !== null ? h : el.getHeight();
38589             this.panelSize = {width: w, height: h};
38590             this.activePanel.setSize(w, h);
38591         }
38592         if(Roo.isIE && this.tabs){
38593             this.tabs.el.repaint();
38594         }
38595     },
38596
38597     /**
38598      * Returns the container element for this region.
38599      * @return {Roo.Element}
38600      */
38601     getEl : function(){
38602         return this.el;
38603     },
38604
38605     /**
38606      * Hides this region.
38607      */
38608     hide : function(){
38609         //if(!this.collapsed){
38610             this.el.dom.style.left = "-2000px";
38611             this.el.hide();
38612         //}else{
38613          //   this.collapsedEl.dom.style.left = "-2000px";
38614          //   this.collapsedEl.hide();
38615        // }
38616         this.visible = false;
38617         this.fireEvent("visibilitychange", this, false);
38618     },
38619
38620     /**
38621      * Shows this region if it was previously hidden.
38622      */
38623     show : function(){
38624         //if(!this.collapsed){
38625             this.el.show();
38626         //}else{
38627         //    this.collapsedEl.show();
38628        // }
38629         this.visible = true;
38630         this.fireEvent("visibilitychange", this, true);
38631     },
38632 /*
38633     closeClicked : function(){
38634         if(this.activePanel){
38635             this.remove(this.activePanel);
38636         }
38637     },
38638
38639     collapseClick : function(e){
38640         if(this.isSlid){
38641            e.stopPropagation();
38642            this.slideIn();
38643         }else{
38644            e.stopPropagation();
38645            this.slideOut();
38646         }
38647     },
38648 */
38649     /**
38650      * Collapses this region.
38651      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38652      */
38653     /*
38654     collapse : function(skipAnim, skipCheck = false){
38655         if(this.collapsed) {
38656             return;
38657         }
38658         
38659         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38660             
38661             this.collapsed = true;
38662             if(this.split){
38663                 this.split.el.hide();
38664             }
38665             if(this.config.animate && skipAnim !== true){
38666                 this.fireEvent("invalidated", this);
38667                 this.animateCollapse();
38668             }else{
38669                 this.el.setLocation(-20000,-20000);
38670                 this.el.hide();
38671                 this.collapsedEl.show();
38672                 this.fireEvent("collapsed", this);
38673                 this.fireEvent("invalidated", this);
38674             }
38675         }
38676         
38677     },
38678 */
38679     animateCollapse : function(){
38680         // overridden
38681     },
38682
38683     /**
38684      * Expands this region if it was previously collapsed.
38685      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38686      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38687      */
38688     /*
38689     expand : function(e, skipAnim){
38690         if(e) {
38691             e.stopPropagation();
38692         }
38693         if(!this.collapsed || this.el.hasActiveFx()) {
38694             return;
38695         }
38696         if(this.isSlid){
38697             this.afterSlideIn();
38698             skipAnim = true;
38699         }
38700         this.collapsed = false;
38701         if(this.config.animate && skipAnim !== true){
38702             this.animateExpand();
38703         }else{
38704             this.el.show();
38705             if(this.split){
38706                 this.split.el.show();
38707             }
38708             this.collapsedEl.setLocation(-2000,-2000);
38709             this.collapsedEl.hide();
38710             this.fireEvent("invalidated", this);
38711             this.fireEvent("expanded", this);
38712         }
38713     },
38714 */
38715     animateExpand : function(){
38716         // overridden
38717     },
38718
38719     initTabs : function()
38720     {
38721         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38722         
38723         var ts = new Roo.bootstrap.panel.Tabs({
38724             el: this.bodyEl.dom,
38725             region : this,
38726             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38727             disableTooltips: this.config.disableTabTips,
38728             toolbar : this.config.toolbar
38729         });
38730         
38731         if(this.config.hideTabs){
38732             ts.stripWrap.setDisplayed(false);
38733         }
38734         this.tabs = ts;
38735         ts.resizeTabs = this.config.resizeTabs === true;
38736         ts.minTabWidth = this.config.minTabWidth || 40;
38737         ts.maxTabWidth = this.config.maxTabWidth || 250;
38738         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38739         ts.monitorResize = false;
38740         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38741         ts.bodyEl.addClass('roo-layout-tabs-body');
38742         this.panels.each(this.initPanelAsTab, this);
38743     },
38744
38745     initPanelAsTab : function(panel){
38746         var ti = this.tabs.addTab(
38747             panel.getEl().id,
38748             panel.getTitle(),
38749             null,
38750             this.config.closeOnTab && panel.isClosable(),
38751             panel.tpl
38752         );
38753         if(panel.tabTip !== undefined){
38754             ti.setTooltip(panel.tabTip);
38755         }
38756         ti.on("activate", function(){
38757               this.setActivePanel(panel);
38758         }, this);
38759         
38760         if(this.config.closeOnTab){
38761             ti.on("beforeclose", function(t, e){
38762                 e.cancel = true;
38763                 this.remove(panel);
38764             }, this);
38765         }
38766         
38767         panel.tabItem = ti;
38768         
38769         return ti;
38770     },
38771
38772     updatePanelTitle : function(panel, title)
38773     {
38774         if(this.activePanel == panel){
38775             this.updateTitle(title);
38776         }
38777         if(this.tabs){
38778             var ti = this.tabs.getTab(panel.getEl().id);
38779             ti.setText(title);
38780             if(panel.tabTip !== undefined){
38781                 ti.setTooltip(panel.tabTip);
38782             }
38783         }
38784     },
38785
38786     updateTitle : function(title){
38787         if(this.titleTextEl && !this.config.title){
38788             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38789         }
38790     },
38791
38792     setActivePanel : function(panel)
38793     {
38794         panel = this.getPanel(panel);
38795         if(this.activePanel && this.activePanel != panel){
38796             if(this.activePanel.setActiveState(false) === false){
38797                 return;
38798             }
38799         }
38800         this.activePanel = panel;
38801         panel.setActiveState(true);
38802         if(this.panelSize){
38803             panel.setSize(this.panelSize.width, this.panelSize.height);
38804         }
38805         if(this.closeBtn){
38806             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38807         }
38808         this.updateTitle(panel.getTitle());
38809         if(this.tabs){
38810             this.fireEvent("invalidated", this);
38811         }
38812         this.fireEvent("panelactivated", this, panel);
38813     },
38814
38815     /**
38816      * Shows the specified panel.
38817      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38818      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38819      */
38820     showPanel : function(panel)
38821     {
38822         panel = this.getPanel(panel);
38823         if(panel){
38824             if(this.tabs){
38825                 var tab = this.tabs.getTab(panel.getEl().id);
38826                 if(tab.isHidden()){
38827                     this.tabs.unhideTab(tab.id);
38828                 }
38829                 tab.activate();
38830             }else{
38831                 this.setActivePanel(panel);
38832             }
38833         }
38834         return panel;
38835     },
38836
38837     /**
38838      * Get the active panel for this region.
38839      * @return {Roo.ContentPanel} The active panel or null
38840      */
38841     getActivePanel : function(){
38842         return this.activePanel;
38843     },
38844
38845     validateVisibility : function(){
38846         if(this.panels.getCount() < 1){
38847             this.updateTitle("&#160;");
38848             this.closeBtn.hide();
38849             this.hide();
38850         }else{
38851             if(!this.isVisible()){
38852                 this.show();
38853             }
38854         }
38855     },
38856
38857     /**
38858      * Adds the passed ContentPanel(s) to this region.
38859      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38860      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38861      */
38862     add : function(panel)
38863     {
38864         if(arguments.length > 1){
38865             for(var i = 0, len = arguments.length; i < len; i++) {
38866                 this.add(arguments[i]);
38867             }
38868             return null;
38869         }
38870         
38871         // if we have not been rendered yet, then we can not really do much of this..
38872         if (!this.bodyEl) {
38873             this.unrendered_panels.push(panel);
38874             return panel;
38875         }
38876         
38877         
38878         
38879         
38880         if(this.hasPanel(panel)){
38881             this.showPanel(panel);
38882             return panel;
38883         }
38884         panel.setRegion(this);
38885         this.panels.add(panel);
38886        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38887             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38888             // and hide them... ???
38889             this.bodyEl.dom.appendChild(panel.getEl().dom);
38890             if(panel.background !== true){
38891                 this.setActivePanel(panel);
38892             }
38893             this.fireEvent("paneladded", this, panel);
38894             return panel;
38895         }
38896         */
38897         if(!this.tabs){
38898             this.initTabs();
38899         }else{
38900             this.initPanelAsTab(panel);
38901         }
38902         
38903         
38904         if(panel.background !== true){
38905             this.tabs.activate(panel.getEl().id);
38906         }
38907         this.fireEvent("paneladded", this, panel);
38908         return panel;
38909     },
38910
38911     /**
38912      * Hides the tab for the specified panel.
38913      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38914      */
38915     hidePanel : function(panel){
38916         if(this.tabs && (panel = this.getPanel(panel))){
38917             this.tabs.hideTab(panel.getEl().id);
38918         }
38919     },
38920
38921     /**
38922      * Unhides the tab for a previously hidden panel.
38923      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38924      */
38925     unhidePanel : function(panel){
38926         if(this.tabs && (panel = this.getPanel(panel))){
38927             this.tabs.unhideTab(panel.getEl().id);
38928         }
38929     },
38930
38931     clearPanels : function(){
38932         while(this.panels.getCount() > 0){
38933              this.remove(this.panels.first());
38934         }
38935     },
38936
38937     /**
38938      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38939      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38940      * @param {Boolean} preservePanel Overrides the config preservePanel option
38941      * @return {Roo.ContentPanel} The panel that was removed
38942      */
38943     remove : function(panel, preservePanel)
38944     {
38945         panel = this.getPanel(panel);
38946         if(!panel){
38947             return null;
38948         }
38949         var e = {};
38950         this.fireEvent("beforeremove", this, panel, e);
38951         if(e.cancel === true){
38952             return null;
38953         }
38954         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38955         var panelId = panel.getId();
38956         this.panels.removeKey(panelId);
38957         if(preservePanel){
38958             document.body.appendChild(panel.getEl().dom);
38959         }
38960         if(this.tabs){
38961             this.tabs.removeTab(panel.getEl().id);
38962         }else if (!preservePanel){
38963             this.bodyEl.dom.removeChild(panel.getEl().dom);
38964         }
38965         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38966             var p = this.panels.first();
38967             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38968             tempEl.appendChild(p.getEl().dom);
38969             this.bodyEl.update("");
38970             this.bodyEl.dom.appendChild(p.getEl().dom);
38971             tempEl = null;
38972             this.updateTitle(p.getTitle());
38973             this.tabs = null;
38974             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38975             this.setActivePanel(p);
38976         }
38977         panel.setRegion(null);
38978         if(this.activePanel == panel){
38979             this.activePanel = null;
38980         }
38981         if(this.config.autoDestroy !== false && preservePanel !== true){
38982             try{panel.destroy();}catch(e){}
38983         }
38984         this.fireEvent("panelremoved", this, panel);
38985         return panel;
38986     },
38987
38988     /**
38989      * Returns the TabPanel component used by this region
38990      * @return {Roo.TabPanel}
38991      */
38992     getTabs : function(){
38993         return this.tabs;
38994     },
38995
38996     createTool : function(parentEl, className){
38997         var btn = Roo.DomHelper.append(parentEl, {
38998             tag: "div",
38999             cls: "x-layout-tools-button",
39000             children: [ {
39001                 tag: "div",
39002                 cls: "roo-layout-tools-button-inner " + className,
39003                 html: "&#160;"
39004             }]
39005         }, true);
39006         btn.addClassOnOver("roo-layout-tools-button-over");
39007         return btn;
39008     }
39009 });/*
39010  * Based on:
39011  * Ext JS Library 1.1.1
39012  * Copyright(c) 2006-2007, Ext JS, LLC.
39013  *
39014  * Originally Released Under LGPL - original licence link has changed is not relivant.
39015  *
39016  * Fork - LGPL
39017  * <script type="text/javascript">
39018  */
39019  
39020
39021
39022 /**
39023  * @class Roo.SplitLayoutRegion
39024  * @extends Roo.LayoutRegion
39025  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39026  */
39027 Roo.bootstrap.layout.Split = function(config){
39028     this.cursor = config.cursor;
39029     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39030 };
39031
39032 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39033 {
39034     splitTip : "Drag to resize.",
39035     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39036     useSplitTips : false,
39037
39038     applyConfig : function(config){
39039         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39040     },
39041     
39042     onRender : function(ctr,pos) {
39043         
39044         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39045         if(!this.config.split){
39046             return;
39047         }
39048         if(!this.split){
39049             
39050             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39051                             tag: "div",
39052                             id: this.el.id + "-split",
39053                             cls: "roo-layout-split roo-layout-split-"+this.position,
39054                             html: "&#160;"
39055             });
39056             /** The SplitBar for this region 
39057             * @type Roo.SplitBar */
39058             // does not exist yet...
39059             Roo.log([this.position, this.orientation]);
39060             
39061             this.split = new Roo.bootstrap.SplitBar({
39062                 dragElement : splitEl,
39063                 resizingElement: this.el,
39064                 orientation : this.orientation
39065             });
39066             
39067             this.split.on("moved", this.onSplitMove, this);
39068             this.split.useShim = this.config.useShim === true;
39069             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39070             if(this.useSplitTips){
39071                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39072             }
39073             //if(config.collapsible){
39074             //    this.split.el.on("dblclick", this.collapse,  this);
39075             //}
39076         }
39077         if(typeof this.config.minSize != "undefined"){
39078             this.split.minSize = this.config.minSize;
39079         }
39080         if(typeof this.config.maxSize != "undefined"){
39081             this.split.maxSize = this.config.maxSize;
39082         }
39083         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39084             this.hideSplitter();
39085         }
39086         
39087     },
39088
39089     getHMaxSize : function(){
39090          var cmax = this.config.maxSize || 10000;
39091          var center = this.mgr.getRegion("center");
39092          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39093     },
39094
39095     getVMaxSize : function(){
39096          var cmax = this.config.maxSize || 10000;
39097          var center = this.mgr.getRegion("center");
39098          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39099     },
39100
39101     onSplitMove : function(split, newSize){
39102         this.fireEvent("resized", this, newSize);
39103     },
39104     
39105     /** 
39106      * Returns the {@link Roo.SplitBar} for this region.
39107      * @return {Roo.SplitBar}
39108      */
39109     getSplitBar : function(){
39110         return this.split;
39111     },
39112     
39113     hide : function(){
39114         this.hideSplitter();
39115         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39116     },
39117
39118     hideSplitter : function(){
39119         if(this.split){
39120             this.split.el.setLocation(-2000,-2000);
39121             this.split.el.hide();
39122         }
39123     },
39124
39125     show : function(){
39126         if(this.split){
39127             this.split.el.show();
39128         }
39129         Roo.bootstrap.layout.Split.superclass.show.call(this);
39130     },
39131     
39132     beforeSlide: function(){
39133         if(Roo.isGecko){// firefox overflow auto bug workaround
39134             this.bodyEl.clip();
39135             if(this.tabs) {
39136                 this.tabs.bodyEl.clip();
39137             }
39138             if(this.activePanel){
39139                 this.activePanel.getEl().clip();
39140                 
39141                 if(this.activePanel.beforeSlide){
39142                     this.activePanel.beforeSlide();
39143                 }
39144             }
39145         }
39146     },
39147     
39148     afterSlide : function(){
39149         if(Roo.isGecko){// firefox overflow auto bug workaround
39150             this.bodyEl.unclip();
39151             if(this.tabs) {
39152                 this.tabs.bodyEl.unclip();
39153             }
39154             if(this.activePanel){
39155                 this.activePanel.getEl().unclip();
39156                 if(this.activePanel.afterSlide){
39157                     this.activePanel.afterSlide();
39158                 }
39159             }
39160         }
39161     },
39162
39163     initAutoHide : function(){
39164         if(this.autoHide !== false){
39165             if(!this.autoHideHd){
39166                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39167                 this.autoHideHd = {
39168                     "mouseout": function(e){
39169                         if(!e.within(this.el, true)){
39170                             st.delay(500);
39171                         }
39172                     },
39173                     "mouseover" : function(e){
39174                         st.cancel();
39175                     },
39176                     scope : this
39177                 };
39178             }
39179             this.el.on(this.autoHideHd);
39180         }
39181     },
39182
39183     clearAutoHide : function(){
39184         if(this.autoHide !== false){
39185             this.el.un("mouseout", this.autoHideHd.mouseout);
39186             this.el.un("mouseover", this.autoHideHd.mouseover);
39187         }
39188     },
39189
39190     clearMonitor : function(){
39191         Roo.get(document).un("click", this.slideInIf, this);
39192     },
39193
39194     // these names are backwards but not changed for compat
39195     slideOut : function(){
39196         if(this.isSlid || this.el.hasActiveFx()){
39197             return;
39198         }
39199         this.isSlid = true;
39200         if(this.collapseBtn){
39201             this.collapseBtn.hide();
39202         }
39203         this.closeBtnState = this.closeBtn.getStyle('display');
39204         this.closeBtn.hide();
39205         if(this.stickBtn){
39206             this.stickBtn.show();
39207         }
39208         this.el.show();
39209         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39210         this.beforeSlide();
39211         this.el.setStyle("z-index", 10001);
39212         this.el.slideIn(this.getSlideAnchor(), {
39213             callback: function(){
39214                 this.afterSlide();
39215                 this.initAutoHide();
39216                 Roo.get(document).on("click", this.slideInIf, this);
39217                 this.fireEvent("slideshow", this);
39218             },
39219             scope: this,
39220             block: true
39221         });
39222     },
39223
39224     afterSlideIn : function(){
39225         this.clearAutoHide();
39226         this.isSlid = false;
39227         this.clearMonitor();
39228         this.el.setStyle("z-index", "");
39229         if(this.collapseBtn){
39230             this.collapseBtn.show();
39231         }
39232         this.closeBtn.setStyle('display', this.closeBtnState);
39233         if(this.stickBtn){
39234             this.stickBtn.hide();
39235         }
39236         this.fireEvent("slidehide", this);
39237     },
39238
39239     slideIn : function(cb){
39240         if(!this.isSlid || this.el.hasActiveFx()){
39241             Roo.callback(cb);
39242             return;
39243         }
39244         this.isSlid = false;
39245         this.beforeSlide();
39246         this.el.slideOut(this.getSlideAnchor(), {
39247             callback: function(){
39248                 this.el.setLeftTop(-10000, -10000);
39249                 this.afterSlide();
39250                 this.afterSlideIn();
39251                 Roo.callback(cb);
39252             },
39253             scope: this,
39254             block: true
39255         });
39256     },
39257     
39258     slideInIf : function(e){
39259         if(!e.within(this.el)){
39260             this.slideIn();
39261         }
39262     },
39263
39264     animateCollapse : function(){
39265         this.beforeSlide();
39266         this.el.setStyle("z-index", 20000);
39267         var anchor = this.getSlideAnchor();
39268         this.el.slideOut(anchor, {
39269             callback : function(){
39270                 this.el.setStyle("z-index", "");
39271                 this.collapsedEl.slideIn(anchor, {duration:.3});
39272                 this.afterSlide();
39273                 this.el.setLocation(-10000,-10000);
39274                 this.el.hide();
39275                 this.fireEvent("collapsed", this);
39276             },
39277             scope: this,
39278             block: true
39279         });
39280     },
39281
39282     animateExpand : function(){
39283         this.beforeSlide();
39284         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39285         this.el.setStyle("z-index", 20000);
39286         this.collapsedEl.hide({
39287             duration:.1
39288         });
39289         this.el.slideIn(this.getSlideAnchor(), {
39290             callback : function(){
39291                 this.el.setStyle("z-index", "");
39292                 this.afterSlide();
39293                 if(this.split){
39294                     this.split.el.show();
39295                 }
39296                 this.fireEvent("invalidated", this);
39297                 this.fireEvent("expanded", this);
39298             },
39299             scope: this,
39300             block: true
39301         });
39302     },
39303
39304     anchors : {
39305         "west" : "left",
39306         "east" : "right",
39307         "north" : "top",
39308         "south" : "bottom"
39309     },
39310
39311     sanchors : {
39312         "west" : "l",
39313         "east" : "r",
39314         "north" : "t",
39315         "south" : "b"
39316     },
39317
39318     canchors : {
39319         "west" : "tl-tr",
39320         "east" : "tr-tl",
39321         "north" : "tl-bl",
39322         "south" : "bl-tl"
39323     },
39324
39325     getAnchor : function(){
39326         return this.anchors[this.position];
39327     },
39328
39329     getCollapseAnchor : function(){
39330         return this.canchors[this.position];
39331     },
39332
39333     getSlideAnchor : function(){
39334         return this.sanchors[this.position];
39335     },
39336
39337     getAlignAdj : function(){
39338         var cm = this.cmargins;
39339         switch(this.position){
39340             case "west":
39341                 return [0, 0];
39342             break;
39343             case "east":
39344                 return [0, 0];
39345             break;
39346             case "north":
39347                 return [0, 0];
39348             break;
39349             case "south":
39350                 return [0, 0];
39351             break;
39352         }
39353     },
39354
39355     getExpandAdj : function(){
39356         var c = this.collapsedEl, cm = this.cmargins;
39357         switch(this.position){
39358             case "west":
39359                 return [-(cm.right+c.getWidth()+cm.left), 0];
39360             break;
39361             case "east":
39362                 return [cm.right+c.getWidth()+cm.left, 0];
39363             break;
39364             case "north":
39365                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39366             break;
39367             case "south":
39368                 return [0, cm.top+cm.bottom+c.getHeight()];
39369             break;
39370         }
39371     }
39372 });/*
39373  * Based on:
39374  * Ext JS Library 1.1.1
39375  * Copyright(c) 2006-2007, Ext JS, LLC.
39376  *
39377  * Originally Released Under LGPL - original licence link has changed is not relivant.
39378  *
39379  * Fork - LGPL
39380  * <script type="text/javascript">
39381  */
39382 /*
39383  * These classes are private internal classes
39384  */
39385 Roo.bootstrap.layout.Center = function(config){
39386     config.region = "center";
39387     Roo.bootstrap.layout.Region.call(this, config);
39388     this.visible = true;
39389     this.minWidth = config.minWidth || 20;
39390     this.minHeight = config.minHeight || 20;
39391 };
39392
39393 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39394     hide : function(){
39395         // center panel can't be hidden
39396     },
39397     
39398     show : function(){
39399         // center panel can't be hidden
39400     },
39401     
39402     getMinWidth: function(){
39403         return this.minWidth;
39404     },
39405     
39406     getMinHeight: function(){
39407         return this.minHeight;
39408     }
39409 });
39410
39411
39412
39413
39414  
39415
39416
39417
39418
39419
39420
39421 Roo.bootstrap.layout.North = function(config)
39422 {
39423     config.region = 'north';
39424     config.cursor = 'n-resize';
39425     
39426     Roo.bootstrap.layout.Split.call(this, config);
39427     
39428     
39429     if(this.split){
39430         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39431         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39432         this.split.el.addClass("roo-layout-split-v");
39433     }
39434     //var size = config.initialSize || config.height;
39435     //if(this.el && typeof size != "undefined"){
39436     //    this.el.setHeight(size);
39437     //}
39438 };
39439 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39440 {
39441     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39442      
39443      
39444     onRender : function(ctr, pos)
39445     {
39446         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39447         var size = this.config.initialSize || this.config.height;
39448         if(this.el && typeof size != "undefined"){
39449             this.el.setHeight(size);
39450         }
39451     
39452     },
39453     
39454     getBox : function(){
39455         if(this.collapsed){
39456             return this.collapsedEl.getBox();
39457         }
39458         var box = this.el.getBox();
39459         if(this.split){
39460             box.height += this.split.el.getHeight();
39461         }
39462         return box;
39463     },
39464     
39465     updateBox : function(box){
39466         if(this.split && !this.collapsed){
39467             box.height -= this.split.el.getHeight();
39468             this.split.el.setLeft(box.x);
39469             this.split.el.setTop(box.y+box.height);
39470             this.split.el.setWidth(box.width);
39471         }
39472         if(this.collapsed){
39473             this.updateBody(box.width, null);
39474         }
39475         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39476     }
39477 });
39478
39479
39480
39481
39482
39483 Roo.bootstrap.layout.South = function(config){
39484     config.region = 'south';
39485     config.cursor = 's-resize';
39486     Roo.bootstrap.layout.Split.call(this, config);
39487     if(this.split){
39488         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39489         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39490         this.split.el.addClass("roo-layout-split-v");
39491     }
39492     
39493 };
39494
39495 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39496     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39497     
39498     onRender : function(ctr, pos)
39499     {
39500         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39501         var size = this.config.initialSize || this.config.height;
39502         if(this.el && typeof size != "undefined"){
39503             this.el.setHeight(size);
39504         }
39505     
39506     },
39507     
39508     getBox : function(){
39509         if(this.collapsed){
39510             return this.collapsedEl.getBox();
39511         }
39512         var box = this.el.getBox();
39513         if(this.split){
39514             var sh = this.split.el.getHeight();
39515             box.height += sh;
39516             box.y -= sh;
39517         }
39518         return box;
39519     },
39520     
39521     updateBox : function(box){
39522         if(this.split && !this.collapsed){
39523             var sh = this.split.el.getHeight();
39524             box.height -= sh;
39525             box.y += sh;
39526             this.split.el.setLeft(box.x);
39527             this.split.el.setTop(box.y-sh);
39528             this.split.el.setWidth(box.width);
39529         }
39530         if(this.collapsed){
39531             this.updateBody(box.width, null);
39532         }
39533         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39534     }
39535 });
39536
39537 Roo.bootstrap.layout.East = function(config){
39538     config.region = "east";
39539     config.cursor = "e-resize";
39540     Roo.bootstrap.layout.Split.call(this, config);
39541     if(this.split){
39542         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39543         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39544         this.split.el.addClass("roo-layout-split-h");
39545     }
39546     
39547 };
39548 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39549     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39550     
39551     onRender : function(ctr, pos)
39552     {
39553         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39554         var size = this.config.initialSize || this.config.width;
39555         if(this.el && typeof size != "undefined"){
39556             this.el.setWidth(size);
39557         }
39558     
39559     },
39560     
39561     getBox : function(){
39562         if(this.collapsed){
39563             return this.collapsedEl.getBox();
39564         }
39565         var box = this.el.getBox();
39566         if(this.split){
39567             var sw = this.split.el.getWidth();
39568             box.width += sw;
39569             box.x -= sw;
39570         }
39571         return box;
39572     },
39573
39574     updateBox : function(box){
39575         if(this.split && !this.collapsed){
39576             var sw = this.split.el.getWidth();
39577             box.width -= sw;
39578             this.split.el.setLeft(box.x);
39579             this.split.el.setTop(box.y);
39580             this.split.el.setHeight(box.height);
39581             box.x += sw;
39582         }
39583         if(this.collapsed){
39584             this.updateBody(null, box.height);
39585         }
39586         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39587     }
39588 });
39589
39590 Roo.bootstrap.layout.West = function(config){
39591     config.region = "west";
39592     config.cursor = "w-resize";
39593     
39594     Roo.bootstrap.layout.Split.call(this, config);
39595     if(this.split){
39596         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39597         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39598         this.split.el.addClass("roo-layout-split-h");
39599     }
39600     
39601 };
39602 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39603     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39604     
39605     onRender: function(ctr, pos)
39606     {
39607         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39608         var size = this.config.initialSize || this.config.width;
39609         if(typeof size != "undefined"){
39610             this.el.setWidth(size);
39611         }
39612     },
39613     
39614     getBox : function(){
39615         if(this.collapsed){
39616             return this.collapsedEl.getBox();
39617         }
39618         var box = this.el.getBox();
39619         if (box.width == 0) {
39620             box.width = this.config.width; // kludge?
39621         }
39622         if(this.split){
39623             box.width += this.split.el.getWidth();
39624         }
39625         return box;
39626     },
39627     
39628     updateBox : function(box){
39629         if(this.split && !this.collapsed){
39630             var sw = this.split.el.getWidth();
39631             box.width -= sw;
39632             this.split.el.setLeft(box.x+box.width);
39633             this.split.el.setTop(box.y);
39634             this.split.el.setHeight(box.height);
39635         }
39636         if(this.collapsed){
39637             this.updateBody(null, box.height);
39638         }
39639         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39640     }
39641 });Roo.namespace("Roo.bootstrap.panel");/*
39642  * Based on:
39643  * Ext JS Library 1.1.1
39644  * Copyright(c) 2006-2007, Ext JS, LLC.
39645  *
39646  * Originally Released Under LGPL - original licence link has changed is not relivant.
39647  *
39648  * Fork - LGPL
39649  * <script type="text/javascript">
39650  */
39651 /**
39652  * @class Roo.ContentPanel
39653  * @extends Roo.util.Observable
39654  * A basic ContentPanel element.
39655  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39656  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39657  * @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
39658  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39659  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39660  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39661  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39662  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39663  * @cfg {String} title          The title for this panel
39664  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39665  * @cfg {String} url            Calls {@link #setUrl} with this value
39666  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39667  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39668  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39669  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39670  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39671  * @cfg {Boolean} badges render the badges
39672  * @cfg {String} cls  extra classes to use  
39673  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39674
39675  * @constructor
39676  * Create a new ContentPanel.
39677  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39678  * @param {String/Object} config A string to set only the title or a config object
39679  * @param {String} content (optional) Set the HTML content for this panel
39680  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39681  */
39682 Roo.bootstrap.panel.Content = function( config){
39683     
39684     this.tpl = config.tpl || false;
39685     
39686     var el = config.el;
39687     var content = config.content;
39688
39689     if(config.autoCreate){ // xtype is available if this is called from factory
39690         el = Roo.id();
39691     }
39692     this.el = Roo.get(el);
39693     if(!this.el && config && config.autoCreate){
39694         if(typeof config.autoCreate == "object"){
39695             if(!config.autoCreate.id){
39696                 config.autoCreate.id = config.id||el;
39697             }
39698             this.el = Roo.DomHelper.append(document.body,
39699                         config.autoCreate, true);
39700         }else{
39701             var elcfg =  {
39702                 tag: "div",
39703                 cls: (config.cls || '') +
39704                     (config.background ? ' bg-' + config.background : '') +
39705                     " roo-layout-inactive-content",
39706                 id: config.id||el
39707             };
39708             if (config.iframe) {
39709                 elcfg.cn = [
39710                     {
39711                         tag : 'iframe',
39712                         style : 'border: 0px',
39713                         src : 'about:blank'
39714                     }
39715                 ];
39716             }
39717               
39718             if (config.html) {
39719                 elcfg.html = config.html;
39720                 
39721             }
39722                         
39723             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39724             if (config.iframe) {
39725                 this.iframeEl = this.el.select('iframe',true).first();
39726             }
39727             
39728         }
39729     } 
39730     this.closable = false;
39731     this.loaded = false;
39732     this.active = false;
39733    
39734       
39735     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39736         
39737         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39738         
39739         this.wrapEl = this.el; //this.el.wrap();
39740         var ti = [];
39741         if (config.toolbar.items) {
39742             ti = config.toolbar.items ;
39743             delete config.toolbar.items ;
39744         }
39745         
39746         var nitems = [];
39747         this.toolbar.render(this.wrapEl, 'before');
39748         for(var i =0;i < ti.length;i++) {
39749           //  Roo.log(['add child', items[i]]);
39750             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39751         }
39752         this.toolbar.items = nitems;
39753         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39754         delete config.toolbar;
39755         
39756     }
39757     /*
39758     // xtype created footer. - not sure if will work as we normally have to render first..
39759     if (this.footer && !this.footer.el && this.footer.xtype) {
39760         if (!this.wrapEl) {
39761             this.wrapEl = this.el.wrap();
39762         }
39763     
39764         this.footer.container = this.wrapEl.createChild();
39765          
39766         this.footer = Roo.factory(this.footer, Roo);
39767         
39768     }
39769     */
39770     
39771      if(typeof config == "string"){
39772         this.title = config;
39773     }else{
39774         Roo.apply(this, config);
39775     }
39776     
39777     if(this.resizeEl){
39778         this.resizeEl = Roo.get(this.resizeEl, true);
39779     }else{
39780         this.resizeEl = this.el;
39781     }
39782     // handle view.xtype
39783     
39784  
39785     
39786     
39787     this.addEvents({
39788         /**
39789          * @event activate
39790          * Fires when this panel is activated. 
39791          * @param {Roo.ContentPanel} this
39792          */
39793         "activate" : true,
39794         /**
39795          * @event deactivate
39796          * Fires when this panel is activated. 
39797          * @param {Roo.ContentPanel} this
39798          */
39799         "deactivate" : true,
39800
39801         /**
39802          * @event resize
39803          * Fires when this panel is resized if fitToFrame is true.
39804          * @param {Roo.ContentPanel} this
39805          * @param {Number} width The width after any component adjustments
39806          * @param {Number} height The height after any component adjustments
39807          */
39808         "resize" : true,
39809         
39810          /**
39811          * @event render
39812          * Fires when this tab is created
39813          * @param {Roo.ContentPanel} this
39814          */
39815         "render" : true
39816         
39817         
39818         
39819     });
39820     
39821
39822     
39823     
39824     if(this.autoScroll && !this.iframe){
39825         this.resizeEl.setStyle("overflow", "auto");
39826     } else {
39827         // fix randome scrolling
39828         //this.el.on('scroll', function() {
39829         //    Roo.log('fix random scolling');
39830         //    this.scrollTo('top',0); 
39831         //});
39832     }
39833     content = content || this.content;
39834     if(content){
39835         this.setContent(content);
39836     }
39837     if(config && config.url){
39838         this.setUrl(this.url, this.params, this.loadOnce);
39839     }
39840     
39841     
39842     
39843     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39844     
39845     if (this.view && typeof(this.view.xtype) != 'undefined') {
39846         this.view.el = this.el.appendChild(document.createElement("div"));
39847         this.view = Roo.factory(this.view); 
39848         this.view.render  &&  this.view.render(false, '');  
39849     }
39850     
39851     
39852     this.fireEvent('render', this);
39853 };
39854
39855 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39856     
39857     cls : '',
39858     background : '',
39859     
39860     tabTip : '',
39861     
39862     iframe : false,
39863     iframeEl : false,
39864     
39865     setRegion : function(region){
39866         this.region = region;
39867         this.setActiveClass(region && !this.background);
39868     },
39869     
39870     
39871     setActiveClass: function(state)
39872     {
39873         if(state){
39874            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39875            this.el.setStyle('position','relative');
39876         }else{
39877            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39878            this.el.setStyle('position', 'absolute');
39879         } 
39880     },
39881     
39882     /**
39883      * Returns the toolbar for this Panel if one was configured. 
39884      * @return {Roo.Toolbar} 
39885      */
39886     getToolbar : function(){
39887         return this.toolbar;
39888     },
39889     
39890     setActiveState : function(active)
39891     {
39892         this.active = active;
39893         this.setActiveClass(active);
39894         if(!active){
39895             if(this.fireEvent("deactivate", this) === false){
39896                 return false;
39897             }
39898             return true;
39899         }
39900         this.fireEvent("activate", this);
39901         return true;
39902     },
39903     /**
39904      * Updates this panel's element (not for iframe)
39905      * @param {String} content The new content
39906      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39907     */
39908     setContent : function(content, loadScripts){
39909         if (this.iframe) {
39910             return;
39911         }
39912         
39913         this.el.update(content, loadScripts);
39914     },
39915
39916     ignoreResize : function(w, h){
39917         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39918             return true;
39919         }else{
39920             this.lastSize = {width: w, height: h};
39921             return false;
39922         }
39923     },
39924     /**
39925      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39926      * @return {Roo.UpdateManager} The UpdateManager
39927      */
39928     getUpdateManager : function(){
39929         if (this.iframe) {
39930             return false;
39931         }
39932         return this.el.getUpdateManager();
39933     },
39934      /**
39935      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39936      * Does not work with IFRAME contents
39937      * @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:
39938 <pre><code>
39939 panel.load({
39940     url: "your-url.php",
39941     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39942     callback: yourFunction,
39943     scope: yourObject, //(optional scope)
39944     discardUrl: false,
39945     nocache: false,
39946     text: "Loading...",
39947     timeout: 30,
39948     scripts: false
39949 });
39950 </code></pre>
39951      
39952      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39953      * 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.
39954      * @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}
39955      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39956      * @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.
39957      * @return {Roo.ContentPanel} this
39958      */
39959     load : function(){
39960         
39961         if (this.iframe) {
39962             return this;
39963         }
39964         
39965         var um = this.el.getUpdateManager();
39966         um.update.apply(um, arguments);
39967         return this;
39968     },
39969
39970
39971     /**
39972      * 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.
39973      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39974      * @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)
39975      * @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)
39976      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39977      */
39978     setUrl : function(url, params, loadOnce){
39979         if (this.iframe) {
39980             this.iframeEl.dom.src = url;
39981             return false;
39982         }
39983         
39984         if(this.refreshDelegate){
39985             this.removeListener("activate", this.refreshDelegate);
39986         }
39987         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39988         this.on("activate", this.refreshDelegate);
39989         return this.el.getUpdateManager();
39990     },
39991     
39992     _handleRefresh : function(url, params, loadOnce){
39993         if(!loadOnce || !this.loaded){
39994             var updater = this.el.getUpdateManager();
39995             updater.update(url, params, this._setLoaded.createDelegate(this));
39996         }
39997     },
39998     
39999     _setLoaded : function(){
40000         this.loaded = true;
40001     }, 
40002     
40003     /**
40004      * Returns this panel's id
40005      * @return {String} 
40006      */
40007     getId : function(){
40008         return this.el.id;
40009     },
40010     
40011     /** 
40012      * Returns this panel's element - used by regiosn to add.
40013      * @return {Roo.Element} 
40014      */
40015     getEl : function(){
40016         return this.wrapEl || this.el;
40017     },
40018     
40019    
40020     
40021     adjustForComponents : function(width, height)
40022     {
40023         //Roo.log('adjustForComponents ');
40024         if(this.resizeEl != this.el){
40025             width -= this.el.getFrameWidth('lr');
40026             height -= this.el.getFrameWidth('tb');
40027         }
40028         if(this.toolbar){
40029             var te = this.toolbar.getEl();
40030             te.setWidth(width);
40031             height -= te.getHeight();
40032         }
40033         if(this.footer){
40034             var te = this.footer.getEl();
40035             te.setWidth(width);
40036             height -= te.getHeight();
40037         }
40038         
40039         
40040         if(this.adjustments){
40041             width += this.adjustments[0];
40042             height += this.adjustments[1];
40043         }
40044         return {"width": width, "height": height};
40045     },
40046     
40047     setSize : function(width, height){
40048         if(this.fitToFrame && !this.ignoreResize(width, height)){
40049             if(this.fitContainer && this.resizeEl != this.el){
40050                 this.el.setSize(width, height);
40051             }
40052             var size = this.adjustForComponents(width, height);
40053             if (this.iframe) {
40054                 this.iframeEl.setSize(width,height);
40055             }
40056             
40057             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40058             this.fireEvent('resize', this, size.width, size.height);
40059             
40060             
40061         }
40062     },
40063     
40064     /**
40065      * Returns this panel's title
40066      * @return {String} 
40067      */
40068     getTitle : function(){
40069         
40070         if (typeof(this.title) != 'object') {
40071             return this.title;
40072         }
40073         
40074         var t = '';
40075         for (var k in this.title) {
40076             if (!this.title.hasOwnProperty(k)) {
40077                 continue;
40078             }
40079             
40080             if (k.indexOf('-') >= 0) {
40081                 var s = k.split('-');
40082                 for (var i = 0; i<s.length; i++) {
40083                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40084                 }
40085             } else {
40086                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40087             }
40088         }
40089         return t;
40090     },
40091     
40092     /**
40093      * Set this panel's title
40094      * @param {String} title
40095      */
40096     setTitle : function(title){
40097         this.title = title;
40098         if(this.region){
40099             this.region.updatePanelTitle(this, title);
40100         }
40101     },
40102     
40103     /**
40104      * Returns true is this panel was configured to be closable
40105      * @return {Boolean} 
40106      */
40107     isClosable : function(){
40108         return this.closable;
40109     },
40110     
40111     beforeSlide : function(){
40112         this.el.clip();
40113         this.resizeEl.clip();
40114     },
40115     
40116     afterSlide : function(){
40117         this.el.unclip();
40118         this.resizeEl.unclip();
40119     },
40120     
40121     /**
40122      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40123      *   Will fail silently if the {@link #setUrl} method has not been called.
40124      *   This does not activate the panel, just updates its content.
40125      */
40126     refresh : function(){
40127         if(this.refreshDelegate){
40128            this.loaded = false;
40129            this.refreshDelegate();
40130         }
40131     },
40132     
40133     /**
40134      * Destroys this panel
40135      */
40136     destroy : function(){
40137         this.el.removeAllListeners();
40138         var tempEl = document.createElement("span");
40139         tempEl.appendChild(this.el.dom);
40140         tempEl.innerHTML = "";
40141         this.el.remove();
40142         this.el = null;
40143     },
40144     
40145     /**
40146      * form - if the content panel contains a form - this is a reference to it.
40147      * @type {Roo.form.Form}
40148      */
40149     form : false,
40150     /**
40151      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40152      *    This contains a reference to it.
40153      * @type {Roo.View}
40154      */
40155     view : false,
40156     
40157       /**
40158      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40159      * <pre><code>
40160
40161 layout.addxtype({
40162        xtype : 'Form',
40163        items: [ .... ]
40164    }
40165 );
40166
40167 </code></pre>
40168      * @param {Object} cfg Xtype definition of item to add.
40169      */
40170     
40171     
40172     getChildContainer: function () {
40173         return this.getEl();
40174     }
40175     
40176     
40177     /*
40178         var  ret = new Roo.factory(cfg);
40179         return ret;
40180         
40181         
40182         // add form..
40183         if (cfg.xtype.match(/^Form$/)) {
40184             
40185             var el;
40186             //if (this.footer) {
40187             //    el = this.footer.container.insertSibling(false, 'before');
40188             //} else {
40189                 el = this.el.createChild();
40190             //}
40191
40192             this.form = new  Roo.form.Form(cfg);
40193             
40194             
40195             if ( this.form.allItems.length) {
40196                 this.form.render(el.dom);
40197             }
40198             return this.form;
40199         }
40200         // should only have one of theses..
40201         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40202             // views.. should not be just added - used named prop 'view''
40203             
40204             cfg.el = this.el.appendChild(document.createElement("div"));
40205             // factory?
40206             
40207             var ret = new Roo.factory(cfg);
40208              
40209              ret.render && ret.render(false, ''); // render blank..
40210             this.view = ret;
40211             return ret;
40212         }
40213         return false;
40214     }
40215     \*/
40216 });
40217  
40218 /**
40219  * @class Roo.bootstrap.panel.Grid
40220  * @extends Roo.bootstrap.panel.Content
40221  * @constructor
40222  * Create a new GridPanel.
40223  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40224  * @param {Object} config A the config object
40225   
40226  */
40227
40228
40229
40230 Roo.bootstrap.panel.Grid = function(config)
40231 {
40232     
40233       
40234     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40235         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40236
40237     config.el = this.wrapper;
40238     //this.el = this.wrapper;
40239     
40240       if (config.container) {
40241         // ctor'ed from a Border/panel.grid
40242         
40243         
40244         this.wrapper.setStyle("overflow", "hidden");
40245         this.wrapper.addClass('roo-grid-container');
40246
40247     }
40248     
40249     
40250     if(config.toolbar){
40251         var tool_el = this.wrapper.createChild();    
40252         this.toolbar = Roo.factory(config.toolbar);
40253         var ti = [];
40254         if (config.toolbar.items) {
40255             ti = config.toolbar.items ;
40256             delete config.toolbar.items ;
40257         }
40258         
40259         var nitems = [];
40260         this.toolbar.render(tool_el);
40261         for(var i =0;i < ti.length;i++) {
40262           //  Roo.log(['add child', items[i]]);
40263             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40264         }
40265         this.toolbar.items = nitems;
40266         
40267         delete config.toolbar;
40268     }
40269     
40270     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40271     config.grid.scrollBody = true;;
40272     config.grid.monitorWindowResize = false; // turn off autosizing
40273     config.grid.autoHeight = false;
40274     config.grid.autoWidth = false;
40275     
40276     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40277     
40278     if (config.background) {
40279         // render grid on panel activation (if panel background)
40280         this.on('activate', function(gp) {
40281             if (!gp.grid.rendered) {
40282                 gp.grid.render(this.wrapper);
40283                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40284             }
40285         });
40286             
40287     } else {
40288         this.grid.render(this.wrapper);
40289         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40290
40291     }
40292     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40293     // ??? needed ??? config.el = this.wrapper;
40294     
40295     
40296     
40297   
40298     // xtype created footer. - not sure if will work as we normally have to render first..
40299     if (this.footer && !this.footer.el && this.footer.xtype) {
40300         
40301         var ctr = this.grid.getView().getFooterPanel(true);
40302         this.footer.dataSource = this.grid.dataSource;
40303         this.footer = Roo.factory(this.footer, Roo);
40304         this.footer.render(ctr);
40305         
40306     }
40307     
40308     
40309     
40310     
40311      
40312 };
40313
40314 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40315     getId : function(){
40316         return this.grid.id;
40317     },
40318     
40319     /**
40320      * Returns the grid for this panel
40321      * @return {Roo.bootstrap.Table} 
40322      */
40323     getGrid : function(){
40324         return this.grid;    
40325     },
40326     
40327     setSize : function(width, height){
40328         if(!this.ignoreResize(width, height)){
40329             var grid = this.grid;
40330             var size = this.adjustForComponents(width, height);
40331             // tfoot is not a footer?
40332           
40333             
40334             var gridel = grid.getGridEl();
40335             gridel.setSize(size.width, size.height);
40336             
40337             var tbd = grid.getGridEl().select('tbody', true).first();
40338             var thd = grid.getGridEl().select('thead',true).first();
40339             var tbf= grid.getGridEl().select('tfoot', true).first();
40340
40341             if (tbf) {
40342                 size.height -= tbf.getHeight();
40343             }
40344             if (thd) {
40345                 size.height -= thd.getHeight();
40346             }
40347             
40348             tbd.setSize(size.width, size.height );
40349             // this is for the account management tab -seems to work there.
40350             var thd = grid.getGridEl().select('thead',true).first();
40351             //if (tbd) {
40352             //    tbd.setSize(size.width, size.height - thd.getHeight());
40353             //}
40354              
40355             grid.autoSize();
40356         }
40357     },
40358      
40359     
40360     
40361     beforeSlide : function(){
40362         this.grid.getView().scroller.clip();
40363     },
40364     
40365     afterSlide : function(){
40366         this.grid.getView().scroller.unclip();
40367     },
40368     
40369     destroy : function(){
40370         this.grid.destroy();
40371         delete this.grid;
40372         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40373     }
40374 });
40375
40376 /**
40377  * @class Roo.bootstrap.panel.Nest
40378  * @extends Roo.bootstrap.panel.Content
40379  * @constructor
40380  * Create a new Panel, that can contain a layout.Border.
40381  * 
40382  * 
40383  * @param {Roo.BorderLayout} layout The layout for this panel
40384  * @param {String/Object} config A string to set only the title or a config object
40385  */
40386 Roo.bootstrap.panel.Nest = function(config)
40387 {
40388     // construct with only one argument..
40389     /* FIXME - implement nicer consturctors
40390     if (layout.layout) {
40391         config = layout;
40392         layout = config.layout;
40393         delete config.layout;
40394     }
40395     if (layout.xtype && !layout.getEl) {
40396         // then layout needs constructing..
40397         layout = Roo.factory(layout, Roo);
40398     }
40399     */
40400     
40401     config.el =  config.layout.getEl();
40402     
40403     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40404     
40405     config.layout.monitorWindowResize = false; // turn off autosizing
40406     this.layout = config.layout;
40407     this.layout.getEl().addClass("roo-layout-nested-layout");
40408     this.layout.parent = this;
40409     
40410     
40411     
40412     
40413 };
40414
40415 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40416
40417     setSize : function(width, height){
40418         if(!this.ignoreResize(width, height)){
40419             var size = this.adjustForComponents(width, height);
40420             var el = this.layout.getEl();
40421             if (size.height < 1) {
40422                 el.setWidth(size.width);   
40423             } else {
40424                 el.setSize(size.width, size.height);
40425             }
40426             var touch = el.dom.offsetWidth;
40427             this.layout.layout();
40428             // ie requires a double layout on the first pass
40429             if(Roo.isIE && !this.initialized){
40430                 this.initialized = true;
40431                 this.layout.layout();
40432             }
40433         }
40434     },
40435     
40436     // activate all subpanels if not currently active..
40437     
40438     setActiveState : function(active){
40439         this.active = active;
40440         this.setActiveClass(active);
40441         
40442         if(!active){
40443             this.fireEvent("deactivate", this);
40444             return;
40445         }
40446         
40447         this.fireEvent("activate", this);
40448         // not sure if this should happen before or after..
40449         if (!this.layout) {
40450             return; // should not happen..
40451         }
40452         var reg = false;
40453         for (var r in this.layout.regions) {
40454             reg = this.layout.getRegion(r);
40455             if (reg.getActivePanel()) {
40456                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40457                 reg.setActivePanel(reg.getActivePanel());
40458                 continue;
40459             }
40460             if (!reg.panels.length) {
40461                 continue;
40462             }
40463             reg.showPanel(reg.getPanel(0));
40464         }
40465         
40466         
40467         
40468         
40469     },
40470     
40471     /**
40472      * Returns the nested BorderLayout for this panel
40473      * @return {Roo.BorderLayout} 
40474      */
40475     getLayout : function(){
40476         return this.layout;
40477     },
40478     
40479      /**
40480      * Adds a xtype elements to the layout of the nested panel
40481      * <pre><code>
40482
40483 panel.addxtype({
40484        xtype : 'ContentPanel',
40485        region: 'west',
40486        items: [ .... ]
40487    }
40488 );
40489
40490 panel.addxtype({
40491         xtype : 'NestedLayoutPanel',
40492         region: 'west',
40493         layout: {
40494            center: { },
40495            west: { }   
40496         },
40497         items : [ ... list of content panels or nested layout panels.. ]
40498    }
40499 );
40500 </code></pre>
40501      * @param {Object} cfg Xtype definition of item to add.
40502      */
40503     addxtype : function(cfg) {
40504         return this.layout.addxtype(cfg);
40505     
40506     }
40507 });/*
40508  * Based on:
40509  * Ext JS Library 1.1.1
40510  * Copyright(c) 2006-2007, Ext JS, LLC.
40511  *
40512  * Originally Released Under LGPL - original licence link has changed is not relivant.
40513  *
40514  * Fork - LGPL
40515  * <script type="text/javascript">
40516  */
40517 /**
40518  * @class Roo.TabPanel
40519  * @extends Roo.util.Observable
40520  * A lightweight tab container.
40521  * <br><br>
40522  * Usage:
40523  * <pre><code>
40524 // basic tabs 1, built from existing content
40525 var tabs = new Roo.TabPanel("tabs1");
40526 tabs.addTab("script", "View Script");
40527 tabs.addTab("markup", "View Markup");
40528 tabs.activate("script");
40529
40530 // more advanced tabs, built from javascript
40531 var jtabs = new Roo.TabPanel("jtabs");
40532 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40533
40534 // set up the UpdateManager
40535 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40536 var updater = tab2.getUpdateManager();
40537 updater.setDefaultUrl("ajax1.htm");
40538 tab2.on('activate', updater.refresh, updater, true);
40539
40540 // Use setUrl for Ajax loading
40541 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40542 tab3.setUrl("ajax2.htm", null, true);
40543
40544 // Disabled tab
40545 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40546 tab4.disable();
40547
40548 jtabs.activate("jtabs-1");
40549  * </code></pre>
40550  * @constructor
40551  * Create a new TabPanel.
40552  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40553  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40554  */
40555 Roo.bootstrap.panel.Tabs = function(config){
40556     /**
40557     * The container element for this TabPanel.
40558     * @type Roo.Element
40559     */
40560     this.el = Roo.get(config.el);
40561     delete config.el;
40562     if(config){
40563         if(typeof config == "boolean"){
40564             this.tabPosition = config ? "bottom" : "top";
40565         }else{
40566             Roo.apply(this, config);
40567         }
40568     }
40569     
40570     if(this.tabPosition == "bottom"){
40571         // if tabs are at the bottom = create the body first.
40572         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40573         this.el.addClass("roo-tabs-bottom");
40574     }
40575     // next create the tabs holders
40576     
40577     if (this.tabPosition == "west"){
40578         
40579         var reg = this.region; // fake it..
40580         while (reg) {
40581             if (!reg.mgr.parent) {
40582                 break;
40583             }
40584             reg = reg.mgr.parent.region;
40585         }
40586         Roo.log("got nest?");
40587         Roo.log(reg);
40588         if (reg.mgr.getRegion('west')) {
40589             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40590             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40591             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40592             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40593             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40594         
40595             
40596         }
40597         
40598         
40599     } else {
40600      
40601         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40602         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40603         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40604         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40605     }
40606     
40607     
40608     if(Roo.isIE){
40609         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40610     }
40611     
40612     // finally - if tabs are at the top, then create the body last..
40613     if(this.tabPosition != "bottom"){
40614         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40615          * @type Roo.Element
40616          */
40617         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40618         this.el.addClass("roo-tabs-top");
40619     }
40620     this.items = [];
40621
40622     this.bodyEl.setStyle("position", "relative");
40623
40624     this.active = null;
40625     this.activateDelegate = this.activate.createDelegate(this);
40626
40627     this.addEvents({
40628         /**
40629          * @event tabchange
40630          * Fires when the active tab changes
40631          * @param {Roo.TabPanel} this
40632          * @param {Roo.TabPanelItem} activePanel The new active tab
40633          */
40634         "tabchange": true,
40635         /**
40636          * @event beforetabchange
40637          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40638          * @param {Roo.TabPanel} this
40639          * @param {Object} e Set cancel to true on this object to cancel the tab change
40640          * @param {Roo.TabPanelItem} tab The tab being changed to
40641          */
40642         "beforetabchange" : true
40643     });
40644
40645     Roo.EventManager.onWindowResize(this.onResize, this);
40646     this.cpad = this.el.getPadding("lr");
40647     this.hiddenCount = 0;
40648
40649
40650     // toolbar on the tabbar support...
40651     if (this.toolbar) {
40652         alert("no toolbar support yet");
40653         this.toolbar  = false;
40654         /*
40655         var tcfg = this.toolbar;
40656         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40657         this.toolbar = new Roo.Toolbar(tcfg);
40658         if (Roo.isSafari) {
40659             var tbl = tcfg.container.child('table', true);
40660             tbl.setAttribute('width', '100%');
40661         }
40662         */
40663         
40664     }
40665    
40666
40667
40668     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40669 };
40670
40671 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40672     /*
40673      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40674      */
40675     tabPosition : "top",
40676     /*
40677      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40678      */
40679     currentTabWidth : 0,
40680     /*
40681      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40682      */
40683     minTabWidth : 40,
40684     /*
40685      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40686      */
40687     maxTabWidth : 250,
40688     /*
40689      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40690      */
40691     preferredTabWidth : 175,
40692     /*
40693      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40694      */
40695     resizeTabs : false,
40696     /*
40697      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40698      */
40699     monitorResize : true,
40700     /*
40701      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40702      */
40703     toolbar : false,  // set by caller..
40704     
40705     region : false, /// set by caller
40706     
40707     disableTooltips : true, // not used yet...
40708
40709     /**
40710      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40711      * @param {String} id The id of the div to use <b>or create</b>
40712      * @param {String} text The text for the tab
40713      * @param {String} content (optional) Content to put in the TabPanelItem body
40714      * @param {Boolean} closable (optional) True to create a close icon on the tab
40715      * @return {Roo.TabPanelItem} The created TabPanelItem
40716      */
40717     addTab : function(id, text, content, closable, tpl)
40718     {
40719         var item = new Roo.bootstrap.panel.TabItem({
40720             panel: this,
40721             id : id,
40722             text : text,
40723             closable : closable,
40724             tpl : tpl
40725         });
40726         this.addTabItem(item);
40727         if(content){
40728             item.setContent(content);
40729         }
40730         return item;
40731     },
40732
40733     /**
40734      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40735      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40736      * @return {Roo.TabPanelItem}
40737      */
40738     getTab : function(id){
40739         return this.items[id];
40740     },
40741
40742     /**
40743      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40744      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40745      */
40746     hideTab : function(id){
40747         var t = this.items[id];
40748         if(!t.isHidden()){
40749            t.setHidden(true);
40750            this.hiddenCount++;
40751            this.autoSizeTabs();
40752         }
40753     },
40754
40755     /**
40756      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40757      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40758      */
40759     unhideTab : function(id){
40760         var t = this.items[id];
40761         if(t.isHidden()){
40762            t.setHidden(false);
40763            this.hiddenCount--;
40764            this.autoSizeTabs();
40765         }
40766     },
40767
40768     /**
40769      * Adds an existing {@link Roo.TabPanelItem}.
40770      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40771      */
40772     addTabItem : function(item)
40773     {
40774         this.items[item.id] = item;
40775         this.items.push(item);
40776         this.autoSizeTabs();
40777       //  if(this.resizeTabs){
40778     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40779   //         this.autoSizeTabs();
40780 //        }else{
40781 //            item.autoSize();
40782        // }
40783     },
40784
40785     /**
40786      * Removes a {@link Roo.TabPanelItem}.
40787      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40788      */
40789     removeTab : function(id){
40790         var items = this.items;
40791         var tab = items[id];
40792         if(!tab) { return; }
40793         var index = items.indexOf(tab);
40794         if(this.active == tab && items.length > 1){
40795             var newTab = this.getNextAvailable(index);
40796             if(newTab) {
40797                 newTab.activate();
40798             }
40799         }
40800         this.stripEl.dom.removeChild(tab.pnode.dom);
40801         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40802             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40803         }
40804         items.splice(index, 1);
40805         delete this.items[tab.id];
40806         tab.fireEvent("close", tab);
40807         tab.purgeListeners();
40808         this.autoSizeTabs();
40809     },
40810
40811     getNextAvailable : function(start){
40812         var items = this.items;
40813         var index = start;
40814         // look for a next tab that will slide over to
40815         // replace the one being removed
40816         while(index < items.length){
40817             var item = items[++index];
40818             if(item && !item.isHidden()){
40819                 return item;
40820             }
40821         }
40822         // if one isn't found select the previous tab (on the left)
40823         index = start;
40824         while(index >= 0){
40825             var item = items[--index];
40826             if(item && !item.isHidden()){
40827                 return item;
40828             }
40829         }
40830         return null;
40831     },
40832
40833     /**
40834      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40835      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40836      */
40837     disableTab : function(id){
40838         var tab = this.items[id];
40839         if(tab && this.active != tab){
40840             tab.disable();
40841         }
40842     },
40843
40844     /**
40845      * Enables a {@link Roo.TabPanelItem} that is disabled.
40846      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40847      */
40848     enableTab : function(id){
40849         var tab = this.items[id];
40850         tab.enable();
40851     },
40852
40853     /**
40854      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40855      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40856      * @return {Roo.TabPanelItem} The TabPanelItem.
40857      */
40858     activate : function(id)
40859     {
40860         //Roo.log('activite:'  + id);
40861         
40862         var tab = this.items[id];
40863         if(!tab){
40864             return null;
40865         }
40866         if(tab == this.active || tab.disabled){
40867             return tab;
40868         }
40869         var e = {};
40870         this.fireEvent("beforetabchange", this, e, tab);
40871         if(e.cancel !== true && !tab.disabled){
40872             if(this.active){
40873                 this.active.hide();
40874             }
40875             this.active = this.items[id];
40876             this.active.show();
40877             this.fireEvent("tabchange", this, this.active);
40878         }
40879         return tab;
40880     },
40881
40882     /**
40883      * Gets the active {@link Roo.TabPanelItem}.
40884      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40885      */
40886     getActiveTab : function(){
40887         return this.active;
40888     },
40889
40890     /**
40891      * Updates the tab body element to fit the height of the container element
40892      * for overflow scrolling
40893      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40894      */
40895     syncHeight : function(targetHeight){
40896         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40897         var bm = this.bodyEl.getMargins();
40898         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40899         this.bodyEl.setHeight(newHeight);
40900         return newHeight;
40901     },
40902
40903     onResize : function(){
40904         if(this.monitorResize){
40905             this.autoSizeTabs();
40906         }
40907     },
40908
40909     /**
40910      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40911      */
40912     beginUpdate : function(){
40913         this.updating = true;
40914     },
40915
40916     /**
40917      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40918      */
40919     endUpdate : function(){
40920         this.updating = false;
40921         this.autoSizeTabs();
40922     },
40923
40924     /**
40925      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40926      */
40927     autoSizeTabs : function()
40928     {
40929         var count = this.items.length;
40930         var vcount = count - this.hiddenCount;
40931         
40932         if (vcount < 2) {
40933             this.stripEl.hide();
40934         } else {
40935             this.stripEl.show();
40936         }
40937         
40938         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40939             return;
40940         }
40941         
40942         
40943         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40944         var availWidth = Math.floor(w / vcount);
40945         var b = this.stripBody;
40946         if(b.getWidth() > w){
40947             var tabs = this.items;
40948             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40949             if(availWidth < this.minTabWidth){
40950                 /*if(!this.sleft){    // incomplete scrolling code
40951                     this.createScrollButtons();
40952                 }
40953                 this.showScroll();
40954                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40955             }
40956         }else{
40957             if(this.currentTabWidth < this.preferredTabWidth){
40958                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40959             }
40960         }
40961     },
40962
40963     /**
40964      * Returns the number of tabs in this TabPanel.
40965      * @return {Number}
40966      */
40967      getCount : function(){
40968          return this.items.length;
40969      },
40970
40971     /**
40972      * Resizes all the tabs to the passed width
40973      * @param {Number} The new width
40974      */
40975     setTabWidth : function(width){
40976         this.currentTabWidth = width;
40977         for(var i = 0, len = this.items.length; i < len; i++) {
40978                 if(!this.items[i].isHidden()) {
40979                 this.items[i].setWidth(width);
40980             }
40981         }
40982     },
40983
40984     /**
40985      * Destroys this TabPanel
40986      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40987      */
40988     destroy : function(removeEl){
40989         Roo.EventManager.removeResizeListener(this.onResize, this);
40990         for(var i = 0, len = this.items.length; i < len; i++){
40991             this.items[i].purgeListeners();
40992         }
40993         if(removeEl === true){
40994             this.el.update("");
40995             this.el.remove();
40996         }
40997     },
40998     
40999     createStrip : function(container)
41000     {
41001         var strip = document.createElement("nav");
41002         strip.className = Roo.bootstrap.version == 4 ?
41003             "navbar-light bg-light" : 
41004             "navbar navbar-default"; //"x-tabs-wrap";
41005         container.appendChild(strip);
41006         return strip;
41007     },
41008     
41009     createStripList : function(strip)
41010     {
41011         // div wrapper for retard IE
41012         // returns the "tr" element.
41013         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41014         //'<div class="x-tabs-strip-wrap">'+
41015           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41016           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41017         return strip.firstChild; //.firstChild.firstChild.firstChild;
41018     },
41019     createBody : function(container)
41020     {
41021         var body = document.createElement("div");
41022         Roo.id(body, "tab-body");
41023         //Roo.fly(body).addClass("x-tabs-body");
41024         Roo.fly(body).addClass("tab-content");
41025         container.appendChild(body);
41026         return body;
41027     },
41028     createItemBody :function(bodyEl, id){
41029         var body = Roo.getDom(id);
41030         if(!body){
41031             body = document.createElement("div");
41032             body.id = id;
41033         }
41034         //Roo.fly(body).addClass("x-tabs-item-body");
41035         Roo.fly(body).addClass("tab-pane");
41036          bodyEl.insertBefore(body, bodyEl.firstChild);
41037         return body;
41038     },
41039     /** @private */
41040     createStripElements :  function(stripEl, text, closable, tpl)
41041     {
41042         var td = document.createElement("li"); // was td..
41043         td.className = 'nav-item';
41044         
41045         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41046         
41047         
41048         stripEl.appendChild(td);
41049         /*if(closable){
41050             td.className = "x-tabs-closable";
41051             if(!this.closeTpl){
41052                 this.closeTpl = new Roo.Template(
41053                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41054                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41055                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41056                 );
41057             }
41058             var el = this.closeTpl.overwrite(td, {"text": text});
41059             var close = el.getElementsByTagName("div")[0];
41060             var inner = el.getElementsByTagName("em")[0];
41061             return {"el": el, "close": close, "inner": inner};
41062         } else {
41063         */
41064         // not sure what this is..
41065 //            if(!this.tabTpl){
41066                 //this.tabTpl = new Roo.Template(
41067                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41068                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41069                 //);
41070 //                this.tabTpl = new Roo.Template(
41071 //                   '<a href="#">' +
41072 //                   '<span unselectable="on"' +
41073 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41074 //                            ' >{text}</span></a>'
41075 //                );
41076 //                
41077 //            }
41078
41079
41080             var template = tpl || this.tabTpl || false;
41081             
41082             if(!template){
41083                 template =  new Roo.Template(
41084                         Roo.bootstrap.version == 4 ? 
41085                             (
41086                                 '<a class="nav-link" href="#" unselectable="on"' +
41087                                      (this.disableTooltips ? '' : ' title="{text}"') +
41088                                      ' >{text}</a>'
41089                             ) : (
41090                                 '<a class="nav-link" href="#">' +
41091                                 '<span unselectable="on"' +
41092                                          (this.disableTooltips ? '' : ' title="{text}"') +
41093                                     ' >{text}</span></a>'
41094                             )
41095                 );
41096             }
41097             
41098             switch (typeof(template)) {
41099                 case 'object' :
41100                     break;
41101                 case 'string' :
41102                     template = new Roo.Template(template);
41103                     break;
41104                 default :
41105                     break;
41106             }
41107             
41108             var el = template.overwrite(td, {"text": text});
41109             
41110             var inner = el.getElementsByTagName("span")[0];
41111             
41112             return {"el": el, "inner": inner};
41113             
41114     }
41115         
41116     
41117 });
41118
41119 /**
41120  * @class Roo.TabPanelItem
41121  * @extends Roo.util.Observable
41122  * Represents an individual item (tab plus body) in a TabPanel.
41123  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41124  * @param {String} id The id of this TabPanelItem
41125  * @param {String} text The text for the tab of this TabPanelItem
41126  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41127  */
41128 Roo.bootstrap.panel.TabItem = function(config){
41129     /**
41130      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41131      * @type Roo.TabPanel
41132      */
41133     this.tabPanel = config.panel;
41134     /**
41135      * The id for this TabPanelItem
41136      * @type String
41137      */
41138     this.id = config.id;
41139     /** @private */
41140     this.disabled = false;
41141     /** @private */
41142     this.text = config.text;
41143     /** @private */
41144     this.loaded = false;
41145     this.closable = config.closable;
41146
41147     /**
41148      * The body element for this TabPanelItem.
41149      * @type Roo.Element
41150      */
41151     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41152     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41153     this.bodyEl.setStyle("display", "block");
41154     this.bodyEl.setStyle("zoom", "1");
41155     //this.hideAction();
41156
41157     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41158     /** @private */
41159     this.el = Roo.get(els.el);
41160     this.inner = Roo.get(els.inner, true);
41161      this.textEl = Roo.bootstrap.version == 4 ?
41162         this.el : Roo.get(this.el.dom.firstChild, true);
41163
41164     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41165     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41166
41167     
41168 //    this.el.on("mousedown", this.onTabMouseDown, this);
41169     this.el.on("click", this.onTabClick, this);
41170     /** @private */
41171     if(config.closable){
41172         var c = Roo.get(els.close, true);
41173         c.dom.title = this.closeText;
41174         c.addClassOnOver("close-over");
41175         c.on("click", this.closeClick, this);
41176      }
41177
41178     this.addEvents({
41179          /**
41180          * @event activate
41181          * Fires when this tab becomes the active tab.
41182          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41183          * @param {Roo.TabPanelItem} this
41184          */
41185         "activate": true,
41186         /**
41187          * @event beforeclose
41188          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41189          * @param {Roo.TabPanelItem} this
41190          * @param {Object} e Set cancel to true on this object to cancel the close.
41191          */
41192         "beforeclose": true,
41193         /**
41194          * @event close
41195          * Fires when this tab is closed.
41196          * @param {Roo.TabPanelItem} this
41197          */
41198          "close": true,
41199         /**
41200          * @event deactivate
41201          * Fires when this tab is no longer the active tab.
41202          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41203          * @param {Roo.TabPanelItem} this
41204          */
41205          "deactivate" : true
41206     });
41207     this.hidden = false;
41208
41209     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41210 };
41211
41212 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41213            {
41214     purgeListeners : function(){
41215        Roo.util.Observable.prototype.purgeListeners.call(this);
41216        this.el.removeAllListeners();
41217     },
41218     /**
41219      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41220      */
41221     show : function(){
41222         this.status_node.addClass("active");
41223         this.showAction();
41224         if(Roo.isOpera){
41225             this.tabPanel.stripWrap.repaint();
41226         }
41227         this.fireEvent("activate", this.tabPanel, this);
41228     },
41229
41230     /**
41231      * Returns true if this tab is the active tab.
41232      * @return {Boolean}
41233      */
41234     isActive : function(){
41235         return this.tabPanel.getActiveTab() == this;
41236     },
41237
41238     /**
41239      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41240      */
41241     hide : function(){
41242         this.status_node.removeClass("active");
41243         this.hideAction();
41244         this.fireEvent("deactivate", this.tabPanel, this);
41245     },
41246
41247     hideAction : function(){
41248         this.bodyEl.hide();
41249         this.bodyEl.setStyle("position", "absolute");
41250         this.bodyEl.setLeft("-20000px");
41251         this.bodyEl.setTop("-20000px");
41252     },
41253
41254     showAction : function(){
41255         this.bodyEl.setStyle("position", "relative");
41256         this.bodyEl.setTop("");
41257         this.bodyEl.setLeft("");
41258         this.bodyEl.show();
41259     },
41260
41261     /**
41262      * Set the tooltip for the tab.
41263      * @param {String} tooltip The tab's tooltip
41264      */
41265     setTooltip : function(text){
41266         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41267             this.textEl.dom.qtip = text;
41268             this.textEl.dom.removeAttribute('title');
41269         }else{
41270             this.textEl.dom.title = text;
41271         }
41272     },
41273
41274     onTabClick : function(e){
41275         e.preventDefault();
41276         this.tabPanel.activate(this.id);
41277     },
41278
41279     onTabMouseDown : function(e){
41280         e.preventDefault();
41281         this.tabPanel.activate(this.id);
41282     },
41283 /*
41284     getWidth : function(){
41285         return this.inner.getWidth();
41286     },
41287
41288     setWidth : function(width){
41289         var iwidth = width - this.linode.getPadding("lr");
41290         this.inner.setWidth(iwidth);
41291         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41292         this.linode.setWidth(width);
41293     },
41294 */
41295     /**
41296      * Show or hide the tab
41297      * @param {Boolean} hidden True to hide or false to show.
41298      */
41299     setHidden : function(hidden){
41300         this.hidden = hidden;
41301         this.linode.setStyle("display", hidden ? "none" : "");
41302     },
41303
41304     /**
41305      * Returns true if this tab is "hidden"
41306      * @return {Boolean}
41307      */
41308     isHidden : function(){
41309         return this.hidden;
41310     },
41311
41312     /**
41313      * Returns the text for this tab
41314      * @return {String}
41315      */
41316     getText : function(){
41317         return this.text;
41318     },
41319     /*
41320     autoSize : function(){
41321         //this.el.beginMeasure();
41322         this.textEl.setWidth(1);
41323         /*
41324          *  #2804 [new] Tabs in Roojs
41325          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41326          */
41327         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41328         //this.el.endMeasure();
41329     //},
41330
41331     /**
41332      * Sets the text for the tab (Note: this also sets the tooltip text)
41333      * @param {String} text The tab's text and tooltip
41334      */
41335     setText : function(text){
41336         this.text = text;
41337         this.textEl.update(text);
41338         this.setTooltip(text);
41339         //if(!this.tabPanel.resizeTabs){
41340         //    this.autoSize();
41341         //}
41342     },
41343     /**
41344      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41345      */
41346     activate : function(){
41347         this.tabPanel.activate(this.id);
41348     },
41349
41350     /**
41351      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41352      */
41353     disable : function(){
41354         if(this.tabPanel.active != this){
41355             this.disabled = true;
41356             this.status_node.addClass("disabled");
41357         }
41358     },
41359
41360     /**
41361      * Enables this TabPanelItem if it was previously disabled.
41362      */
41363     enable : function(){
41364         this.disabled = false;
41365         this.status_node.removeClass("disabled");
41366     },
41367
41368     /**
41369      * Sets the content for this TabPanelItem.
41370      * @param {String} content The content
41371      * @param {Boolean} loadScripts true to look for and load scripts
41372      */
41373     setContent : function(content, loadScripts){
41374         this.bodyEl.update(content, loadScripts);
41375     },
41376
41377     /**
41378      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41379      * @return {Roo.UpdateManager} The UpdateManager
41380      */
41381     getUpdateManager : function(){
41382         return this.bodyEl.getUpdateManager();
41383     },
41384
41385     /**
41386      * Set a URL to be used to load the content for this TabPanelItem.
41387      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41388      * @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)
41389      * @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)
41390      * @return {Roo.UpdateManager} The UpdateManager
41391      */
41392     setUrl : function(url, params, loadOnce){
41393         if(this.refreshDelegate){
41394             this.un('activate', this.refreshDelegate);
41395         }
41396         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41397         this.on("activate", this.refreshDelegate);
41398         return this.bodyEl.getUpdateManager();
41399     },
41400
41401     /** @private */
41402     _handleRefresh : function(url, params, loadOnce){
41403         if(!loadOnce || !this.loaded){
41404             var updater = this.bodyEl.getUpdateManager();
41405             updater.update(url, params, this._setLoaded.createDelegate(this));
41406         }
41407     },
41408
41409     /**
41410      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41411      *   Will fail silently if the setUrl method has not been called.
41412      *   This does not activate the panel, just updates its content.
41413      */
41414     refresh : function(){
41415         if(this.refreshDelegate){
41416            this.loaded = false;
41417            this.refreshDelegate();
41418         }
41419     },
41420
41421     /** @private */
41422     _setLoaded : function(){
41423         this.loaded = true;
41424     },
41425
41426     /** @private */
41427     closeClick : function(e){
41428         var o = {};
41429         e.stopEvent();
41430         this.fireEvent("beforeclose", this, o);
41431         if(o.cancel !== true){
41432             this.tabPanel.removeTab(this.id);
41433         }
41434     },
41435     /**
41436      * The text displayed in the tooltip for the close icon.
41437      * @type String
41438      */
41439     closeText : "Close this tab"
41440 });
41441 /**
41442 *    This script refer to:
41443 *    Title: International Telephone Input
41444 *    Author: Jack O'Connor
41445 *    Code version:  v12.1.12
41446 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41447 **/
41448
41449 Roo.bootstrap.PhoneInputData = function() {
41450     var d = [
41451       [
41452         "Afghanistan (‫افغانستان‬‎)",
41453         "af",
41454         "93"
41455       ],
41456       [
41457         "Albania (Shqipëri)",
41458         "al",
41459         "355"
41460       ],
41461       [
41462         "Algeria (‫الجزائر‬‎)",
41463         "dz",
41464         "213"
41465       ],
41466       [
41467         "American Samoa",
41468         "as",
41469         "1684"
41470       ],
41471       [
41472         "Andorra",
41473         "ad",
41474         "376"
41475       ],
41476       [
41477         "Angola",
41478         "ao",
41479         "244"
41480       ],
41481       [
41482         "Anguilla",
41483         "ai",
41484         "1264"
41485       ],
41486       [
41487         "Antigua and Barbuda",
41488         "ag",
41489         "1268"
41490       ],
41491       [
41492         "Argentina",
41493         "ar",
41494         "54"
41495       ],
41496       [
41497         "Armenia (Հայաստան)",
41498         "am",
41499         "374"
41500       ],
41501       [
41502         "Aruba",
41503         "aw",
41504         "297"
41505       ],
41506       [
41507         "Australia",
41508         "au",
41509         "61",
41510         0
41511       ],
41512       [
41513         "Austria (Österreich)",
41514         "at",
41515         "43"
41516       ],
41517       [
41518         "Azerbaijan (Azərbaycan)",
41519         "az",
41520         "994"
41521       ],
41522       [
41523         "Bahamas",
41524         "bs",
41525         "1242"
41526       ],
41527       [
41528         "Bahrain (‫البحرين‬‎)",
41529         "bh",
41530         "973"
41531       ],
41532       [
41533         "Bangladesh (বাংলাদেশ)",
41534         "bd",
41535         "880"
41536       ],
41537       [
41538         "Barbados",
41539         "bb",
41540         "1246"
41541       ],
41542       [
41543         "Belarus (Беларусь)",
41544         "by",
41545         "375"
41546       ],
41547       [
41548         "Belgium (België)",
41549         "be",
41550         "32"
41551       ],
41552       [
41553         "Belize",
41554         "bz",
41555         "501"
41556       ],
41557       [
41558         "Benin (Bénin)",
41559         "bj",
41560         "229"
41561       ],
41562       [
41563         "Bermuda",
41564         "bm",
41565         "1441"
41566       ],
41567       [
41568         "Bhutan (འབྲུག)",
41569         "bt",
41570         "975"
41571       ],
41572       [
41573         "Bolivia",
41574         "bo",
41575         "591"
41576       ],
41577       [
41578         "Bosnia and Herzegovina (Босна и Херцеговина)",
41579         "ba",
41580         "387"
41581       ],
41582       [
41583         "Botswana",
41584         "bw",
41585         "267"
41586       ],
41587       [
41588         "Brazil (Brasil)",
41589         "br",
41590         "55"
41591       ],
41592       [
41593         "British Indian Ocean Territory",
41594         "io",
41595         "246"
41596       ],
41597       [
41598         "British Virgin Islands",
41599         "vg",
41600         "1284"
41601       ],
41602       [
41603         "Brunei",
41604         "bn",
41605         "673"
41606       ],
41607       [
41608         "Bulgaria (България)",
41609         "bg",
41610         "359"
41611       ],
41612       [
41613         "Burkina Faso",
41614         "bf",
41615         "226"
41616       ],
41617       [
41618         "Burundi (Uburundi)",
41619         "bi",
41620         "257"
41621       ],
41622       [
41623         "Cambodia (កម្ពុជា)",
41624         "kh",
41625         "855"
41626       ],
41627       [
41628         "Cameroon (Cameroun)",
41629         "cm",
41630         "237"
41631       ],
41632       [
41633         "Canada",
41634         "ca",
41635         "1",
41636         1,
41637         ["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"]
41638       ],
41639       [
41640         "Cape Verde (Kabu Verdi)",
41641         "cv",
41642         "238"
41643       ],
41644       [
41645         "Caribbean Netherlands",
41646         "bq",
41647         "599",
41648         1
41649       ],
41650       [
41651         "Cayman Islands",
41652         "ky",
41653         "1345"
41654       ],
41655       [
41656         "Central African Republic (République centrafricaine)",
41657         "cf",
41658         "236"
41659       ],
41660       [
41661         "Chad (Tchad)",
41662         "td",
41663         "235"
41664       ],
41665       [
41666         "Chile",
41667         "cl",
41668         "56"
41669       ],
41670       [
41671         "China (中国)",
41672         "cn",
41673         "86"
41674       ],
41675       [
41676         "Christmas Island",
41677         "cx",
41678         "61",
41679         2
41680       ],
41681       [
41682         "Cocos (Keeling) Islands",
41683         "cc",
41684         "61",
41685         1
41686       ],
41687       [
41688         "Colombia",
41689         "co",
41690         "57"
41691       ],
41692       [
41693         "Comoros (‫جزر القمر‬‎)",
41694         "km",
41695         "269"
41696       ],
41697       [
41698         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41699         "cd",
41700         "243"
41701       ],
41702       [
41703         "Congo (Republic) (Congo-Brazzaville)",
41704         "cg",
41705         "242"
41706       ],
41707       [
41708         "Cook Islands",
41709         "ck",
41710         "682"
41711       ],
41712       [
41713         "Costa Rica",
41714         "cr",
41715         "506"
41716       ],
41717       [
41718         "Côte d’Ivoire",
41719         "ci",
41720         "225"
41721       ],
41722       [
41723         "Croatia (Hrvatska)",
41724         "hr",
41725         "385"
41726       ],
41727       [
41728         "Cuba",
41729         "cu",
41730         "53"
41731       ],
41732       [
41733         "Curaçao",
41734         "cw",
41735         "599",
41736         0
41737       ],
41738       [
41739         "Cyprus (Κύπρος)",
41740         "cy",
41741         "357"
41742       ],
41743       [
41744         "Czech Republic (Česká republika)",
41745         "cz",
41746         "420"
41747       ],
41748       [
41749         "Denmark (Danmark)",
41750         "dk",
41751         "45"
41752       ],
41753       [
41754         "Djibouti",
41755         "dj",
41756         "253"
41757       ],
41758       [
41759         "Dominica",
41760         "dm",
41761         "1767"
41762       ],
41763       [
41764         "Dominican Republic (República Dominicana)",
41765         "do",
41766         "1",
41767         2,
41768         ["809", "829", "849"]
41769       ],
41770       [
41771         "Ecuador",
41772         "ec",
41773         "593"
41774       ],
41775       [
41776         "Egypt (‫مصر‬‎)",
41777         "eg",
41778         "20"
41779       ],
41780       [
41781         "El Salvador",
41782         "sv",
41783         "503"
41784       ],
41785       [
41786         "Equatorial Guinea (Guinea Ecuatorial)",
41787         "gq",
41788         "240"
41789       ],
41790       [
41791         "Eritrea",
41792         "er",
41793         "291"
41794       ],
41795       [
41796         "Estonia (Eesti)",
41797         "ee",
41798         "372"
41799       ],
41800       [
41801         "Ethiopia",
41802         "et",
41803         "251"
41804       ],
41805       [
41806         "Falkland Islands (Islas Malvinas)",
41807         "fk",
41808         "500"
41809       ],
41810       [
41811         "Faroe Islands (Føroyar)",
41812         "fo",
41813         "298"
41814       ],
41815       [
41816         "Fiji",
41817         "fj",
41818         "679"
41819       ],
41820       [
41821         "Finland (Suomi)",
41822         "fi",
41823         "358",
41824         0
41825       ],
41826       [
41827         "France",
41828         "fr",
41829         "33"
41830       ],
41831       [
41832         "French Guiana (Guyane française)",
41833         "gf",
41834         "594"
41835       ],
41836       [
41837         "French Polynesia (Polynésie française)",
41838         "pf",
41839         "689"
41840       ],
41841       [
41842         "Gabon",
41843         "ga",
41844         "241"
41845       ],
41846       [
41847         "Gambia",
41848         "gm",
41849         "220"
41850       ],
41851       [
41852         "Georgia (საქართველო)",
41853         "ge",
41854         "995"
41855       ],
41856       [
41857         "Germany (Deutschland)",
41858         "de",
41859         "49"
41860       ],
41861       [
41862         "Ghana (Gaana)",
41863         "gh",
41864         "233"
41865       ],
41866       [
41867         "Gibraltar",
41868         "gi",
41869         "350"
41870       ],
41871       [
41872         "Greece (Ελλάδα)",
41873         "gr",
41874         "30"
41875       ],
41876       [
41877         "Greenland (Kalaallit Nunaat)",
41878         "gl",
41879         "299"
41880       ],
41881       [
41882         "Grenada",
41883         "gd",
41884         "1473"
41885       ],
41886       [
41887         "Guadeloupe",
41888         "gp",
41889         "590",
41890         0
41891       ],
41892       [
41893         "Guam",
41894         "gu",
41895         "1671"
41896       ],
41897       [
41898         "Guatemala",
41899         "gt",
41900         "502"
41901       ],
41902       [
41903         "Guernsey",
41904         "gg",
41905         "44",
41906         1
41907       ],
41908       [
41909         "Guinea (Guinée)",
41910         "gn",
41911         "224"
41912       ],
41913       [
41914         "Guinea-Bissau (Guiné Bissau)",
41915         "gw",
41916         "245"
41917       ],
41918       [
41919         "Guyana",
41920         "gy",
41921         "592"
41922       ],
41923       [
41924         "Haiti",
41925         "ht",
41926         "509"
41927       ],
41928       [
41929         "Honduras",
41930         "hn",
41931         "504"
41932       ],
41933       [
41934         "Hong Kong (香港)",
41935         "hk",
41936         "852"
41937       ],
41938       [
41939         "Hungary (Magyarország)",
41940         "hu",
41941         "36"
41942       ],
41943       [
41944         "Iceland (Ísland)",
41945         "is",
41946         "354"
41947       ],
41948       [
41949         "India (भारत)",
41950         "in",
41951         "91"
41952       ],
41953       [
41954         "Indonesia",
41955         "id",
41956         "62"
41957       ],
41958       [
41959         "Iran (‫ایران‬‎)",
41960         "ir",
41961         "98"
41962       ],
41963       [
41964         "Iraq (‫العراق‬‎)",
41965         "iq",
41966         "964"
41967       ],
41968       [
41969         "Ireland",
41970         "ie",
41971         "353"
41972       ],
41973       [
41974         "Isle of Man",
41975         "im",
41976         "44",
41977         2
41978       ],
41979       [
41980         "Israel (‫ישראל‬‎)",
41981         "il",
41982         "972"
41983       ],
41984       [
41985         "Italy (Italia)",
41986         "it",
41987         "39",
41988         0
41989       ],
41990       [
41991         "Jamaica",
41992         "jm",
41993         "1876"
41994       ],
41995       [
41996         "Japan (日本)",
41997         "jp",
41998         "81"
41999       ],
42000       [
42001         "Jersey",
42002         "je",
42003         "44",
42004         3
42005       ],
42006       [
42007         "Jordan (‫الأردن‬‎)",
42008         "jo",
42009         "962"
42010       ],
42011       [
42012         "Kazakhstan (Казахстан)",
42013         "kz",
42014         "7",
42015         1
42016       ],
42017       [
42018         "Kenya",
42019         "ke",
42020         "254"
42021       ],
42022       [
42023         "Kiribati",
42024         "ki",
42025         "686"
42026       ],
42027       [
42028         "Kosovo",
42029         "xk",
42030         "383"
42031       ],
42032       [
42033         "Kuwait (‫الكويت‬‎)",
42034         "kw",
42035         "965"
42036       ],
42037       [
42038         "Kyrgyzstan (Кыргызстан)",
42039         "kg",
42040         "996"
42041       ],
42042       [
42043         "Laos (ລາວ)",
42044         "la",
42045         "856"
42046       ],
42047       [
42048         "Latvia (Latvija)",
42049         "lv",
42050         "371"
42051       ],
42052       [
42053         "Lebanon (‫لبنان‬‎)",
42054         "lb",
42055         "961"
42056       ],
42057       [
42058         "Lesotho",
42059         "ls",
42060         "266"
42061       ],
42062       [
42063         "Liberia",
42064         "lr",
42065         "231"
42066       ],
42067       [
42068         "Libya (‫ليبيا‬‎)",
42069         "ly",
42070         "218"
42071       ],
42072       [
42073         "Liechtenstein",
42074         "li",
42075         "423"
42076       ],
42077       [
42078         "Lithuania (Lietuva)",
42079         "lt",
42080         "370"
42081       ],
42082       [
42083         "Luxembourg",
42084         "lu",
42085         "352"
42086       ],
42087       [
42088         "Macau (澳門)",
42089         "mo",
42090         "853"
42091       ],
42092       [
42093         "Macedonia (FYROM) (Македонија)",
42094         "mk",
42095         "389"
42096       ],
42097       [
42098         "Madagascar (Madagasikara)",
42099         "mg",
42100         "261"
42101       ],
42102       [
42103         "Malawi",
42104         "mw",
42105         "265"
42106       ],
42107       [
42108         "Malaysia",
42109         "my",
42110         "60"
42111       ],
42112       [
42113         "Maldives",
42114         "mv",
42115         "960"
42116       ],
42117       [
42118         "Mali",
42119         "ml",
42120         "223"
42121       ],
42122       [
42123         "Malta",
42124         "mt",
42125         "356"
42126       ],
42127       [
42128         "Marshall Islands",
42129         "mh",
42130         "692"
42131       ],
42132       [
42133         "Martinique",
42134         "mq",
42135         "596"
42136       ],
42137       [
42138         "Mauritania (‫موريتانيا‬‎)",
42139         "mr",
42140         "222"
42141       ],
42142       [
42143         "Mauritius (Moris)",
42144         "mu",
42145         "230"
42146       ],
42147       [
42148         "Mayotte",
42149         "yt",
42150         "262",
42151         1
42152       ],
42153       [
42154         "Mexico (México)",
42155         "mx",
42156         "52"
42157       ],
42158       [
42159         "Micronesia",
42160         "fm",
42161         "691"
42162       ],
42163       [
42164         "Moldova (Republica Moldova)",
42165         "md",
42166         "373"
42167       ],
42168       [
42169         "Monaco",
42170         "mc",
42171         "377"
42172       ],
42173       [
42174         "Mongolia (Монгол)",
42175         "mn",
42176         "976"
42177       ],
42178       [
42179         "Montenegro (Crna Gora)",
42180         "me",
42181         "382"
42182       ],
42183       [
42184         "Montserrat",
42185         "ms",
42186         "1664"
42187       ],
42188       [
42189         "Morocco (‫المغرب‬‎)",
42190         "ma",
42191         "212",
42192         0
42193       ],
42194       [
42195         "Mozambique (Moçambique)",
42196         "mz",
42197         "258"
42198       ],
42199       [
42200         "Myanmar (Burma) (မြန်မာ)",
42201         "mm",
42202         "95"
42203       ],
42204       [
42205         "Namibia (Namibië)",
42206         "na",
42207         "264"
42208       ],
42209       [
42210         "Nauru",
42211         "nr",
42212         "674"
42213       ],
42214       [
42215         "Nepal (नेपाल)",
42216         "np",
42217         "977"
42218       ],
42219       [
42220         "Netherlands (Nederland)",
42221         "nl",
42222         "31"
42223       ],
42224       [
42225         "New Caledonia (Nouvelle-Calédonie)",
42226         "nc",
42227         "687"
42228       ],
42229       [
42230         "New Zealand",
42231         "nz",
42232         "64"
42233       ],
42234       [
42235         "Nicaragua",
42236         "ni",
42237         "505"
42238       ],
42239       [
42240         "Niger (Nijar)",
42241         "ne",
42242         "227"
42243       ],
42244       [
42245         "Nigeria",
42246         "ng",
42247         "234"
42248       ],
42249       [
42250         "Niue",
42251         "nu",
42252         "683"
42253       ],
42254       [
42255         "Norfolk Island",
42256         "nf",
42257         "672"
42258       ],
42259       [
42260         "North Korea (조선 민주주의 인민 공화국)",
42261         "kp",
42262         "850"
42263       ],
42264       [
42265         "Northern Mariana Islands",
42266         "mp",
42267         "1670"
42268       ],
42269       [
42270         "Norway (Norge)",
42271         "no",
42272         "47",
42273         0
42274       ],
42275       [
42276         "Oman (‫عُمان‬‎)",
42277         "om",
42278         "968"
42279       ],
42280       [
42281         "Pakistan (‫پاکستان‬‎)",
42282         "pk",
42283         "92"
42284       ],
42285       [
42286         "Palau",
42287         "pw",
42288         "680"
42289       ],
42290       [
42291         "Palestine (‫فلسطين‬‎)",
42292         "ps",
42293         "970"
42294       ],
42295       [
42296         "Panama (Panamá)",
42297         "pa",
42298         "507"
42299       ],
42300       [
42301         "Papua New Guinea",
42302         "pg",
42303         "675"
42304       ],
42305       [
42306         "Paraguay",
42307         "py",
42308         "595"
42309       ],
42310       [
42311         "Peru (Perú)",
42312         "pe",
42313         "51"
42314       ],
42315       [
42316         "Philippines",
42317         "ph",
42318         "63"
42319       ],
42320       [
42321         "Poland (Polska)",
42322         "pl",
42323         "48"
42324       ],
42325       [
42326         "Portugal",
42327         "pt",
42328         "351"
42329       ],
42330       [
42331         "Puerto Rico",
42332         "pr",
42333         "1",
42334         3,
42335         ["787", "939"]
42336       ],
42337       [
42338         "Qatar (‫قطر‬‎)",
42339         "qa",
42340         "974"
42341       ],
42342       [
42343         "Réunion (La Réunion)",
42344         "re",
42345         "262",
42346         0
42347       ],
42348       [
42349         "Romania (România)",
42350         "ro",
42351         "40"
42352       ],
42353       [
42354         "Russia (Россия)",
42355         "ru",
42356         "7",
42357         0
42358       ],
42359       [
42360         "Rwanda",
42361         "rw",
42362         "250"
42363       ],
42364       [
42365         "Saint Barthélemy",
42366         "bl",
42367         "590",
42368         1
42369       ],
42370       [
42371         "Saint Helena",
42372         "sh",
42373         "290"
42374       ],
42375       [
42376         "Saint Kitts and Nevis",
42377         "kn",
42378         "1869"
42379       ],
42380       [
42381         "Saint Lucia",
42382         "lc",
42383         "1758"
42384       ],
42385       [
42386         "Saint Martin (Saint-Martin (partie française))",
42387         "mf",
42388         "590",
42389         2
42390       ],
42391       [
42392         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42393         "pm",
42394         "508"
42395       ],
42396       [
42397         "Saint Vincent and the Grenadines",
42398         "vc",
42399         "1784"
42400       ],
42401       [
42402         "Samoa",
42403         "ws",
42404         "685"
42405       ],
42406       [
42407         "San Marino",
42408         "sm",
42409         "378"
42410       ],
42411       [
42412         "São Tomé and Príncipe (São Tomé e Príncipe)",
42413         "st",
42414         "239"
42415       ],
42416       [
42417         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42418         "sa",
42419         "966"
42420       ],
42421       [
42422         "Senegal (Sénégal)",
42423         "sn",
42424         "221"
42425       ],
42426       [
42427         "Serbia (Србија)",
42428         "rs",
42429         "381"
42430       ],
42431       [
42432         "Seychelles",
42433         "sc",
42434         "248"
42435       ],
42436       [
42437         "Sierra Leone",
42438         "sl",
42439         "232"
42440       ],
42441       [
42442         "Singapore",
42443         "sg",
42444         "65"
42445       ],
42446       [
42447         "Sint Maarten",
42448         "sx",
42449         "1721"
42450       ],
42451       [
42452         "Slovakia (Slovensko)",
42453         "sk",
42454         "421"
42455       ],
42456       [
42457         "Slovenia (Slovenija)",
42458         "si",
42459         "386"
42460       ],
42461       [
42462         "Solomon Islands",
42463         "sb",
42464         "677"
42465       ],
42466       [
42467         "Somalia (Soomaaliya)",
42468         "so",
42469         "252"
42470       ],
42471       [
42472         "South Africa",
42473         "za",
42474         "27"
42475       ],
42476       [
42477         "South Korea (대한민국)",
42478         "kr",
42479         "82"
42480       ],
42481       [
42482         "South Sudan (‫جنوب السودان‬‎)",
42483         "ss",
42484         "211"
42485       ],
42486       [
42487         "Spain (España)",
42488         "es",
42489         "34"
42490       ],
42491       [
42492         "Sri Lanka (ශ්‍රී ලංකාව)",
42493         "lk",
42494         "94"
42495       ],
42496       [
42497         "Sudan (‫السودان‬‎)",
42498         "sd",
42499         "249"
42500       ],
42501       [
42502         "Suriname",
42503         "sr",
42504         "597"
42505       ],
42506       [
42507         "Svalbard and Jan Mayen",
42508         "sj",
42509         "47",
42510         1
42511       ],
42512       [
42513         "Swaziland",
42514         "sz",
42515         "268"
42516       ],
42517       [
42518         "Sweden (Sverige)",
42519         "se",
42520         "46"
42521       ],
42522       [
42523         "Switzerland (Schweiz)",
42524         "ch",
42525         "41"
42526       ],
42527       [
42528         "Syria (‫سوريا‬‎)",
42529         "sy",
42530         "963"
42531       ],
42532       [
42533         "Taiwan (台灣)",
42534         "tw",
42535         "886"
42536       ],
42537       [
42538         "Tajikistan",
42539         "tj",
42540         "992"
42541       ],
42542       [
42543         "Tanzania",
42544         "tz",
42545         "255"
42546       ],
42547       [
42548         "Thailand (ไทย)",
42549         "th",
42550         "66"
42551       ],
42552       [
42553         "Timor-Leste",
42554         "tl",
42555         "670"
42556       ],
42557       [
42558         "Togo",
42559         "tg",
42560         "228"
42561       ],
42562       [
42563         "Tokelau",
42564         "tk",
42565         "690"
42566       ],
42567       [
42568         "Tonga",
42569         "to",
42570         "676"
42571       ],
42572       [
42573         "Trinidad and Tobago",
42574         "tt",
42575         "1868"
42576       ],
42577       [
42578         "Tunisia (‫تونس‬‎)",
42579         "tn",
42580         "216"
42581       ],
42582       [
42583         "Turkey (Türkiye)",
42584         "tr",
42585         "90"
42586       ],
42587       [
42588         "Turkmenistan",
42589         "tm",
42590         "993"
42591       ],
42592       [
42593         "Turks and Caicos Islands",
42594         "tc",
42595         "1649"
42596       ],
42597       [
42598         "Tuvalu",
42599         "tv",
42600         "688"
42601       ],
42602       [
42603         "U.S. Virgin Islands",
42604         "vi",
42605         "1340"
42606       ],
42607       [
42608         "Uganda",
42609         "ug",
42610         "256"
42611       ],
42612       [
42613         "Ukraine (Україна)",
42614         "ua",
42615         "380"
42616       ],
42617       [
42618         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42619         "ae",
42620         "971"
42621       ],
42622       [
42623         "United Kingdom",
42624         "gb",
42625         "44",
42626         0
42627       ],
42628       [
42629         "United States",
42630         "us",
42631         "1",
42632         0
42633       ],
42634       [
42635         "Uruguay",
42636         "uy",
42637         "598"
42638       ],
42639       [
42640         "Uzbekistan (Oʻzbekiston)",
42641         "uz",
42642         "998"
42643       ],
42644       [
42645         "Vanuatu",
42646         "vu",
42647         "678"
42648       ],
42649       [
42650         "Vatican City (Città del Vaticano)",
42651         "va",
42652         "39",
42653         1
42654       ],
42655       [
42656         "Venezuela",
42657         "ve",
42658         "58"
42659       ],
42660       [
42661         "Vietnam (Việt Nam)",
42662         "vn",
42663         "84"
42664       ],
42665       [
42666         "Wallis and Futuna (Wallis-et-Futuna)",
42667         "wf",
42668         "681"
42669       ],
42670       [
42671         "Western Sahara (‫الصحراء الغربية‬‎)",
42672         "eh",
42673         "212",
42674         1
42675       ],
42676       [
42677         "Yemen (‫اليمن‬‎)",
42678         "ye",
42679         "967"
42680       ],
42681       [
42682         "Zambia",
42683         "zm",
42684         "260"
42685       ],
42686       [
42687         "Zimbabwe",
42688         "zw",
42689         "263"
42690       ],
42691       [
42692         "Åland Islands",
42693         "ax",
42694         "358",
42695         1
42696       ]
42697   ];
42698   
42699   return d;
42700 }/**
42701 *    This script refer to:
42702 *    Title: International Telephone Input
42703 *    Author: Jack O'Connor
42704 *    Code version:  v12.1.12
42705 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42706 **/
42707
42708 /**
42709  * @class Roo.bootstrap.PhoneInput
42710  * @extends Roo.bootstrap.TriggerField
42711  * An input with International dial-code selection
42712  
42713  * @cfg {String} defaultDialCode default '+852'
42714  * @cfg {Array} preferedCountries default []
42715   
42716  * @constructor
42717  * Create a new PhoneInput.
42718  * @param {Object} config Configuration options
42719  */
42720
42721 Roo.bootstrap.PhoneInput = function(config) {
42722     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42723 };
42724
42725 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42726         
42727         listWidth: undefined,
42728         
42729         selectedClass: 'active',
42730         
42731         invalidClass : "has-warning",
42732         
42733         validClass: 'has-success',
42734         
42735         allowed: '0123456789',
42736         
42737         max_length: 15,
42738         
42739         /**
42740          * @cfg {String} defaultDialCode The default dial code when initializing the input
42741          */
42742         defaultDialCode: '+852',
42743         
42744         /**
42745          * @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
42746          */
42747         preferedCountries: false,
42748         
42749         getAutoCreate : function()
42750         {
42751             var data = Roo.bootstrap.PhoneInputData();
42752             var align = this.labelAlign || this.parentLabelAlign();
42753             var id = Roo.id();
42754             
42755             this.allCountries = [];
42756             this.dialCodeMapping = [];
42757             
42758             for (var i = 0; i < data.length; i++) {
42759               var c = data[i];
42760               this.allCountries[i] = {
42761                 name: c[0],
42762                 iso2: c[1],
42763                 dialCode: c[2],
42764                 priority: c[3] || 0,
42765                 areaCodes: c[4] || null
42766               };
42767               this.dialCodeMapping[c[2]] = {
42768                   name: c[0],
42769                   iso2: c[1],
42770                   priority: c[3] || 0,
42771                   areaCodes: c[4] || null
42772               };
42773             }
42774             
42775             var cfg = {
42776                 cls: 'form-group',
42777                 cn: []
42778             };
42779             
42780             var input =  {
42781                 tag: 'input',
42782                 id : id,
42783                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42784                 maxlength: this.max_length,
42785                 cls : 'form-control tel-input',
42786                 autocomplete: 'new-password'
42787             };
42788             
42789             var hiddenInput = {
42790                 tag: 'input',
42791                 type: 'hidden',
42792                 cls: 'hidden-tel-input'
42793             };
42794             
42795             if (this.name) {
42796                 hiddenInput.name = this.name;
42797             }
42798             
42799             if (this.disabled) {
42800                 input.disabled = true;
42801             }
42802             
42803             var flag_container = {
42804                 tag: 'div',
42805                 cls: 'flag-box',
42806                 cn: [
42807                     {
42808                         tag: 'div',
42809                         cls: 'flag'
42810                     },
42811                     {
42812                         tag: 'div',
42813                         cls: 'caret'
42814                     }
42815                 ]
42816             };
42817             
42818             var box = {
42819                 tag: 'div',
42820                 cls: this.hasFeedback ? 'has-feedback' : '',
42821                 cn: [
42822                     hiddenInput,
42823                     input,
42824                     {
42825                         tag: 'input',
42826                         cls: 'dial-code-holder',
42827                         disabled: true
42828                     }
42829                 ]
42830             };
42831             
42832             var container = {
42833                 cls: 'roo-select2-container input-group',
42834                 cn: [
42835                     flag_container,
42836                     box
42837                 ]
42838             };
42839             
42840             if (this.fieldLabel.length) {
42841                 var indicator = {
42842                     tag: 'i',
42843                     tooltip: 'This field is required'
42844                 };
42845                 
42846                 var label = {
42847                     tag: 'label',
42848                     'for':  id,
42849                     cls: 'control-label',
42850                     cn: []
42851                 };
42852                 
42853                 var label_text = {
42854                     tag: 'span',
42855                     html: this.fieldLabel
42856                 };
42857                 
42858                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42859                 label.cn = [
42860                     indicator,
42861                     label_text
42862                 ];
42863                 
42864                 if(this.indicatorpos == 'right') {
42865                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42866                     label.cn = [
42867                         label_text,
42868                         indicator
42869                     ];
42870                 }
42871                 
42872                 if(align == 'left') {
42873                     container = {
42874                         tag: 'div',
42875                         cn: [
42876                             container
42877                         ]
42878                     };
42879                     
42880                     if(this.labelWidth > 12){
42881                         label.style = "width: " + this.labelWidth + 'px';
42882                     }
42883                     if(this.labelWidth < 13 && this.labelmd == 0){
42884                         this.labelmd = this.labelWidth;
42885                     }
42886                     if(this.labellg > 0){
42887                         label.cls += ' col-lg-' + this.labellg;
42888                         input.cls += ' col-lg-' + (12 - this.labellg);
42889                     }
42890                     if(this.labelmd > 0){
42891                         label.cls += ' col-md-' + this.labelmd;
42892                         container.cls += ' col-md-' + (12 - this.labelmd);
42893                     }
42894                     if(this.labelsm > 0){
42895                         label.cls += ' col-sm-' + this.labelsm;
42896                         container.cls += ' col-sm-' + (12 - this.labelsm);
42897                     }
42898                     if(this.labelxs > 0){
42899                         label.cls += ' col-xs-' + this.labelxs;
42900                         container.cls += ' col-xs-' + (12 - this.labelxs);
42901                     }
42902                 }
42903             }
42904             
42905             cfg.cn = [
42906                 label,
42907                 container
42908             ];
42909             
42910             var settings = this;
42911             
42912             ['xs','sm','md','lg'].map(function(size){
42913                 if (settings[size]) {
42914                     cfg.cls += ' col-' + size + '-' + settings[size];
42915                 }
42916             });
42917             
42918             this.store = new Roo.data.Store({
42919                 proxy : new Roo.data.MemoryProxy({}),
42920                 reader : new Roo.data.JsonReader({
42921                     fields : [
42922                         {
42923                             'name' : 'name',
42924                             'type' : 'string'
42925                         },
42926                         {
42927                             'name' : 'iso2',
42928                             'type' : 'string'
42929                         },
42930                         {
42931                             'name' : 'dialCode',
42932                             'type' : 'string'
42933                         },
42934                         {
42935                             'name' : 'priority',
42936                             'type' : 'string'
42937                         },
42938                         {
42939                             'name' : 'areaCodes',
42940                             'type' : 'string'
42941                         }
42942                     ]
42943                 })
42944             });
42945             
42946             if(!this.preferedCountries) {
42947                 this.preferedCountries = [
42948                     'hk',
42949                     'gb',
42950                     'us'
42951                 ];
42952             }
42953             
42954             var p = this.preferedCountries.reverse();
42955             
42956             if(p) {
42957                 for (var i = 0; i < p.length; i++) {
42958                     for (var j = 0; j < this.allCountries.length; j++) {
42959                         if(this.allCountries[j].iso2 == p[i]) {
42960                             var t = this.allCountries[j];
42961                             this.allCountries.splice(j,1);
42962                             this.allCountries.unshift(t);
42963                         }
42964                     } 
42965                 }
42966             }
42967             
42968             this.store.proxy.data = {
42969                 success: true,
42970                 data: this.allCountries
42971             };
42972             
42973             return cfg;
42974         },
42975         
42976         initEvents : function()
42977         {
42978             this.createList();
42979             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42980             
42981             this.indicator = this.indicatorEl();
42982             this.flag = this.flagEl();
42983             this.dialCodeHolder = this.dialCodeHolderEl();
42984             
42985             this.trigger = this.el.select('div.flag-box',true).first();
42986             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42987             
42988             var _this = this;
42989             
42990             (function(){
42991                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42992                 _this.list.setWidth(lw);
42993             }).defer(100);
42994             
42995             this.list.on('mouseover', this.onViewOver, this);
42996             this.list.on('mousemove', this.onViewMove, this);
42997             this.inputEl().on("keyup", this.onKeyUp, this);
42998             this.inputEl().on("keypress", this.onKeyPress, this);
42999             
43000             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43001
43002             this.view = new Roo.View(this.list, this.tpl, {
43003                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43004             });
43005             
43006             this.view.on('click', this.onViewClick, this);
43007             this.setValue(this.defaultDialCode);
43008         },
43009         
43010         onTriggerClick : function(e)
43011         {
43012             Roo.log('trigger click');
43013             if(this.disabled){
43014                 return;
43015             }
43016             
43017             if(this.isExpanded()){
43018                 this.collapse();
43019                 this.hasFocus = false;
43020             }else {
43021                 this.store.load({});
43022                 this.hasFocus = true;
43023                 this.expand();
43024             }
43025         },
43026         
43027         isExpanded : function()
43028         {
43029             return this.list.isVisible();
43030         },
43031         
43032         collapse : function()
43033         {
43034             if(!this.isExpanded()){
43035                 return;
43036             }
43037             this.list.hide();
43038             Roo.get(document).un('mousedown', this.collapseIf, this);
43039             Roo.get(document).un('mousewheel', this.collapseIf, this);
43040             this.fireEvent('collapse', this);
43041             this.validate();
43042         },
43043         
43044         expand : function()
43045         {
43046             Roo.log('expand');
43047
43048             if(this.isExpanded() || !this.hasFocus){
43049                 return;
43050             }
43051             
43052             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43053             this.list.setWidth(lw);
43054             
43055             this.list.show();
43056             this.restrictHeight();
43057             
43058             Roo.get(document).on('mousedown', this.collapseIf, this);
43059             Roo.get(document).on('mousewheel', this.collapseIf, this);
43060             
43061             this.fireEvent('expand', this);
43062         },
43063         
43064         restrictHeight : function()
43065         {
43066             this.list.alignTo(this.inputEl(), this.listAlign);
43067             this.list.alignTo(this.inputEl(), this.listAlign);
43068         },
43069         
43070         onViewOver : function(e, t)
43071         {
43072             if(this.inKeyMode){
43073                 return;
43074             }
43075             var item = this.view.findItemFromChild(t);
43076             
43077             if(item){
43078                 var index = this.view.indexOf(item);
43079                 this.select(index, false);
43080             }
43081         },
43082
43083         // private
43084         onViewClick : function(view, doFocus, el, e)
43085         {
43086             var index = this.view.getSelectedIndexes()[0];
43087             
43088             var r = this.store.getAt(index);
43089             
43090             if(r){
43091                 this.onSelect(r, index);
43092             }
43093             if(doFocus !== false && !this.blockFocus){
43094                 this.inputEl().focus();
43095             }
43096         },
43097         
43098         onViewMove : function(e, t)
43099         {
43100             this.inKeyMode = false;
43101         },
43102         
43103         select : function(index, scrollIntoView)
43104         {
43105             this.selectedIndex = index;
43106             this.view.select(index);
43107             if(scrollIntoView !== false){
43108                 var el = this.view.getNode(index);
43109                 if(el){
43110                     this.list.scrollChildIntoView(el, false);
43111                 }
43112             }
43113         },
43114         
43115         createList : function()
43116         {
43117             this.list = Roo.get(document.body).createChild({
43118                 tag: 'ul',
43119                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43120                 style: 'display:none'
43121             });
43122             
43123             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43124         },
43125         
43126         collapseIf : function(e)
43127         {
43128             var in_combo  = e.within(this.el);
43129             var in_list =  e.within(this.list);
43130             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43131             
43132             if (in_combo || in_list || is_list) {
43133                 return;
43134             }
43135             this.collapse();
43136         },
43137         
43138         onSelect : function(record, index)
43139         {
43140             if(this.fireEvent('beforeselect', this, record, index) !== false){
43141                 
43142                 this.setFlagClass(record.data.iso2);
43143                 this.setDialCode(record.data.dialCode);
43144                 this.hasFocus = false;
43145                 this.collapse();
43146                 this.fireEvent('select', this, record, index);
43147             }
43148         },
43149         
43150         flagEl : function()
43151         {
43152             var flag = this.el.select('div.flag',true).first();
43153             if(!flag){
43154                 return false;
43155             }
43156             return flag;
43157         },
43158         
43159         dialCodeHolderEl : function()
43160         {
43161             var d = this.el.select('input.dial-code-holder',true).first();
43162             if(!d){
43163                 return false;
43164             }
43165             return d;
43166         },
43167         
43168         setDialCode : function(v)
43169         {
43170             this.dialCodeHolder.dom.value = '+'+v;
43171         },
43172         
43173         setFlagClass : function(n)
43174         {
43175             this.flag.dom.className = 'flag '+n;
43176         },
43177         
43178         getValue : function()
43179         {
43180             var v = this.inputEl().getValue();
43181             if(this.dialCodeHolder) {
43182                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43183             }
43184             return v;
43185         },
43186         
43187         setValue : function(v)
43188         {
43189             var d = this.getDialCode(v);
43190             
43191             //invalid dial code
43192             if(v.length == 0 || !d || d.length == 0) {
43193                 if(this.rendered){
43194                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43195                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43196                 }
43197                 return;
43198             }
43199             
43200             //valid dial code
43201             this.setFlagClass(this.dialCodeMapping[d].iso2);
43202             this.setDialCode(d);
43203             this.inputEl().dom.value = v.replace('+'+d,'');
43204             this.hiddenEl().dom.value = this.getValue();
43205             
43206             this.validate();
43207         },
43208         
43209         getDialCode : function(v)
43210         {
43211             v = v ||  '';
43212             
43213             if (v.length == 0) {
43214                 return this.dialCodeHolder.dom.value;
43215             }
43216             
43217             var dialCode = "";
43218             if (v.charAt(0) != "+") {
43219                 return false;
43220             }
43221             var numericChars = "";
43222             for (var i = 1; i < v.length; i++) {
43223               var c = v.charAt(i);
43224               if (!isNaN(c)) {
43225                 numericChars += c;
43226                 if (this.dialCodeMapping[numericChars]) {
43227                   dialCode = v.substr(1, i);
43228                 }
43229                 if (numericChars.length == 4) {
43230                   break;
43231                 }
43232               }
43233             }
43234             return dialCode;
43235         },
43236         
43237         reset : function()
43238         {
43239             this.setValue(this.defaultDialCode);
43240             this.validate();
43241         },
43242         
43243         hiddenEl : function()
43244         {
43245             return this.el.select('input.hidden-tel-input',true).first();
43246         },
43247         
43248         // after setting val
43249         onKeyUp : function(e){
43250             this.setValue(this.getValue());
43251         },
43252         
43253         onKeyPress : function(e){
43254             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43255                 e.stopEvent();
43256             }
43257         }
43258         
43259 });
43260 /**
43261  * @class Roo.bootstrap.MoneyField
43262  * @extends Roo.bootstrap.ComboBox
43263  * Bootstrap MoneyField class
43264  * 
43265  * @constructor
43266  * Create a new MoneyField.
43267  * @param {Object} config Configuration options
43268  */
43269
43270 Roo.bootstrap.MoneyField = function(config) {
43271     
43272     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43273     
43274 };
43275
43276 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43277     
43278     /**
43279      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43280      */
43281     allowDecimals : true,
43282     /**
43283      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43284      */
43285     decimalSeparator : ".",
43286     /**
43287      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43288      */
43289     decimalPrecision : 0,
43290     /**
43291      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43292      */
43293     allowNegative : true,
43294     /**
43295      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43296      */
43297     allowZero: true,
43298     /**
43299      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43300      */
43301     minValue : Number.NEGATIVE_INFINITY,
43302     /**
43303      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43304      */
43305     maxValue : Number.MAX_VALUE,
43306     /**
43307      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43308      */
43309     minText : "The minimum value for this field is {0}",
43310     /**
43311      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43312      */
43313     maxText : "The maximum value for this field is {0}",
43314     /**
43315      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43316      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43317      */
43318     nanText : "{0} is not a valid number",
43319     /**
43320      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43321      */
43322     castInt : true,
43323     /**
43324      * @cfg {String} defaults currency of the MoneyField
43325      * value should be in lkey
43326      */
43327     defaultCurrency : false,
43328     /**
43329      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43330      */
43331     thousandsDelimiter : false,
43332     /**
43333      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43334      */
43335     max_length: false,
43336     
43337     inputlg : 9,
43338     inputmd : 9,
43339     inputsm : 9,
43340     inputxs : 6,
43341     
43342     store : false,
43343     
43344     getAutoCreate : function()
43345     {
43346         var align = this.labelAlign || this.parentLabelAlign();
43347         
43348         var id = Roo.id();
43349
43350         var cfg = {
43351             cls: 'form-group',
43352             cn: []
43353         };
43354
43355         var input =  {
43356             tag: 'input',
43357             id : id,
43358             cls : 'form-control roo-money-amount-input',
43359             autocomplete: 'new-password'
43360         };
43361         
43362         var hiddenInput = {
43363             tag: 'input',
43364             type: 'hidden',
43365             id: Roo.id(),
43366             cls: 'hidden-number-input'
43367         };
43368         
43369         if(this.max_length) {
43370             input.maxlength = this.max_length; 
43371         }
43372         
43373         if (this.name) {
43374             hiddenInput.name = this.name;
43375         }
43376
43377         if (this.disabled) {
43378             input.disabled = true;
43379         }
43380
43381         var clg = 12 - this.inputlg;
43382         var cmd = 12 - this.inputmd;
43383         var csm = 12 - this.inputsm;
43384         var cxs = 12 - this.inputxs;
43385         
43386         var container = {
43387             tag : 'div',
43388             cls : 'row roo-money-field',
43389             cn : [
43390                 {
43391                     tag : 'div',
43392                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43393                     cn : [
43394                         {
43395                             tag : 'div',
43396                             cls: 'roo-select2-container input-group',
43397                             cn: [
43398                                 {
43399                                     tag : 'input',
43400                                     cls : 'form-control roo-money-currency-input',
43401                                     autocomplete: 'new-password',
43402                                     readOnly : 1,
43403                                     name : this.currencyName
43404                                 },
43405                                 {
43406                                     tag :'span',
43407                                     cls : 'input-group-addon',
43408                                     cn : [
43409                                         {
43410                                             tag: 'span',
43411                                             cls: 'caret'
43412                                         }
43413                                     ]
43414                                 }
43415                             ]
43416                         }
43417                     ]
43418                 },
43419                 {
43420                     tag : 'div',
43421                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43422                     cn : [
43423                         {
43424                             tag: 'div',
43425                             cls: this.hasFeedback ? 'has-feedback' : '',
43426                             cn: [
43427                                 input
43428                             ]
43429                         }
43430                     ]
43431                 }
43432             ]
43433             
43434         };
43435         
43436         if (this.fieldLabel.length) {
43437             var indicator = {
43438                 tag: 'i',
43439                 tooltip: 'This field is required'
43440             };
43441
43442             var label = {
43443                 tag: 'label',
43444                 'for':  id,
43445                 cls: 'control-label',
43446                 cn: []
43447             };
43448
43449             var label_text = {
43450                 tag: 'span',
43451                 html: this.fieldLabel
43452             };
43453
43454             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43455             label.cn = [
43456                 indicator,
43457                 label_text
43458             ];
43459
43460             if(this.indicatorpos == 'right') {
43461                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43462                 label.cn = [
43463                     label_text,
43464                     indicator
43465                 ];
43466             }
43467
43468             if(align == 'left') {
43469                 container = {
43470                     tag: 'div',
43471                     cn: [
43472                         container
43473                     ]
43474                 };
43475
43476                 if(this.labelWidth > 12){
43477                     label.style = "width: " + this.labelWidth + 'px';
43478                 }
43479                 if(this.labelWidth < 13 && this.labelmd == 0){
43480                     this.labelmd = this.labelWidth;
43481                 }
43482                 if(this.labellg > 0){
43483                     label.cls += ' col-lg-' + this.labellg;
43484                     input.cls += ' col-lg-' + (12 - this.labellg);
43485                 }
43486                 if(this.labelmd > 0){
43487                     label.cls += ' col-md-' + this.labelmd;
43488                     container.cls += ' col-md-' + (12 - this.labelmd);
43489                 }
43490                 if(this.labelsm > 0){
43491                     label.cls += ' col-sm-' + this.labelsm;
43492                     container.cls += ' col-sm-' + (12 - this.labelsm);
43493                 }
43494                 if(this.labelxs > 0){
43495                     label.cls += ' col-xs-' + this.labelxs;
43496                     container.cls += ' col-xs-' + (12 - this.labelxs);
43497                 }
43498             }
43499         }
43500
43501         cfg.cn = [
43502             label,
43503             container,
43504             hiddenInput
43505         ];
43506         
43507         var settings = this;
43508
43509         ['xs','sm','md','lg'].map(function(size){
43510             if (settings[size]) {
43511                 cfg.cls += ' col-' + size + '-' + settings[size];
43512             }
43513         });
43514         
43515         return cfg;
43516     },
43517     
43518     initEvents : function()
43519     {
43520         this.indicator = this.indicatorEl();
43521         
43522         this.initCurrencyEvent();
43523         
43524         this.initNumberEvent();
43525     },
43526     
43527     initCurrencyEvent : function()
43528     {
43529         if (!this.store) {
43530             throw "can not find store for combo";
43531         }
43532         
43533         this.store = Roo.factory(this.store, Roo.data);
43534         this.store.parent = this;
43535         
43536         this.createList();
43537         
43538         this.triggerEl = this.el.select('.input-group-addon', true).first();
43539         
43540         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43541         
43542         var _this = this;
43543         
43544         (function(){
43545             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43546             _this.list.setWidth(lw);
43547         }).defer(100);
43548         
43549         this.list.on('mouseover', this.onViewOver, this);
43550         this.list.on('mousemove', this.onViewMove, this);
43551         this.list.on('scroll', this.onViewScroll, this);
43552         
43553         if(!this.tpl){
43554             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43555         }
43556         
43557         this.view = new Roo.View(this.list, this.tpl, {
43558             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43559         });
43560         
43561         this.view.on('click', this.onViewClick, this);
43562         
43563         this.store.on('beforeload', this.onBeforeLoad, this);
43564         this.store.on('load', this.onLoad, this);
43565         this.store.on('loadexception', this.onLoadException, this);
43566         
43567         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43568             "up" : function(e){
43569                 this.inKeyMode = true;
43570                 this.selectPrev();
43571             },
43572
43573             "down" : function(e){
43574                 if(!this.isExpanded()){
43575                     this.onTriggerClick();
43576                 }else{
43577                     this.inKeyMode = true;
43578                     this.selectNext();
43579                 }
43580             },
43581
43582             "enter" : function(e){
43583                 this.collapse();
43584                 
43585                 if(this.fireEvent("specialkey", this, e)){
43586                     this.onViewClick(false);
43587                 }
43588                 
43589                 return true;
43590             },
43591
43592             "esc" : function(e){
43593                 this.collapse();
43594             },
43595
43596             "tab" : function(e){
43597                 this.collapse();
43598                 
43599                 if(this.fireEvent("specialkey", this, e)){
43600                     this.onViewClick(false);
43601                 }
43602                 
43603                 return true;
43604             },
43605
43606             scope : this,
43607
43608             doRelay : function(foo, bar, hname){
43609                 if(hname == 'down' || this.scope.isExpanded()){
43610                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43611                 }
43612                 return true;
43613             },
43614
43615             forceKeyDown: true
43616         });
43617         
43618         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43619         
43620     },
43621     
43622     initNumberEvent : function(e)
43623     {
43624         this.inputEl().on("keydown" , this.fireKey,  this);
43625         this.inputEl().on("focus", this.onFocus,  this);
43626         this.inputEl().on("blur", this.onBlur,  this);
43627         
43628         this.inputEl().relayEvent('keyup', this);
43629         
43630         if(this.indicator){
43631             this.indicator.addClass('invisible');
43632         }
43633  
43634         this.originalValue = this.getValue();
43635         
43636         if(this.validationEvent == 'keyup'){
43637             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43638             this.inputEl().on('keyup', this.filterValidation, this);
43639         }
43640         else if(this.validationEvent !== false){
43641             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43642         }
43643         
43644         if(this.selectOnFocus){
43645             this.on("focus", this.preFocus, this);
43646             
43647         }
43648         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43649             this.inputEl().on("keypress", this.filterKeys, this);
43650         } else {
43651             this.inputEl().relayEvent('keypress', this);
43652         }
43653         
43654         var allowed = "0123456789";
43655         
43656         if(this.allowDecimals){
43657             allowed += this.decimalSeparator;
43658         }
43659         
43660         if(this.allowNegative){
43661             allowed += "-";
43662         }
43663         
43664         if(this.thousandsDelimiter) {
43665             allowed += ",";
43666         }
43667         
43668         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43669         
43670         var keyPress = function(e){
43671             
43672             var k = e.getKey();
43673             
43674             var c = e.getCharCode();
43675             
43676             if(
43677                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43678                     allowed.indexOf(String.fromCharCode(c)) === -1
43679             ){
43680                 e.stopEvent();
43681                 return;
43682             }
43683             
43684             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43685                 return;
43686             }
43687             
43688             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43689                 e.stopEvent();
43690             }
43691         };
43692         
43693         this.inputEl().on("keypress", keyPress, this);
43694         
43695     },
43696     
43697     onTriggerClick : function(e)
43698     {   
43699         if(this.disabled){
43700             return;
43701         }
43702         
43703         this.page = 0;
43704         this.loadNext = false;
43705         
43706         if(this.isExpanded()){
43707             this.collapse();
43708             return;
43709         }
43710         
43711         this.hasFocus = true;
43712         
43713         if(this.triggerAction == 'all') {
43714             this.doQuery(this.allQuery, true);
43715             return;
43716         }
43717         
43718         this.doQuery(this.getRawValue());
43719     },
43720     
43721     getCurrency : function()
43722     {   
43723         var v = this.currencyEl().getValue();
43724         
43725         return v;
43726     },
43727     
43728     restrictHeight : function()
43729     {
43730         this.list.alignTo(this.currencyEl(), this.listAlign);
43731         this.list.alignTo(this.currencyEl(), this.listAlign);
43732     },
43733     
43734     onViewClick : function(view, doFocus, el, e)
43735     {
43736         var index = this.view.getSelectedIndexes()[0];
43737         
43738         var r = this.store.getAt(index);
43739         
43740         if(r){
43741             this.onSelect(r, index);
43742         }
43743     },
43744     
43745     onSelect : function(record, index){
43746         
43747         if(this.fireEvent('beforeselect', this, record, index) !== false){
43748         
43749             this.setFromCurrencyData(index > -1 ? record.data : false);
43750             
43751             this.collapse();
43752             
43753             this.fireEvent('select', this, record, index);
43754         }
43755     },
43756     
43757     setFromCurrencyData : function(o)
43758     {
43759         var currency = '';
43760         
43761         this.lastCurrency = o;
43762         
43763         if (this.currencyField) {
43764             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43765         } else {
43766             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43767         }
43768         
43769         this.lastSelectionText = currency;
43770         
43771         //setting default currency
43772         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43773             this.setCurrency(this.defaultCurrency);
43774             return;
43775         }
43776         
43777         this.setCurrency(currency);
43778     },
43779     
43780     setFromData : function(o)
43781     {
43782         var c = {};
43783         
43784         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43785         
43786         this.setFromCurrencyData(c);
43787         
43788         var value = '';
43789         
43790         if (this.name) {
43791             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43792         } else {
43793             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43794         }
43795         
43796         this.setValue(value);
43797         
43798     },
43799     
43800     setCurrency : function(v)
43801     {   
43802         this.currencyValue = v;
43803         
43804         if(this.rendered){
43805             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43806             this.validate();
43807         }
43808     },
43809     
43810     setValue : function(v)
43811     {
43812         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43813         
43814         this.value = v;
43815         
43816         if(this.rendered){
43817             
43818             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43819             
43820             this.inputEl().dom.value = (v == '') ? '' :
43821                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43822             
43823             if(!this.allowZero && v === '0') {
43824                 this.hiddenEl().dom.value = '';
43825                 this.inputEl().dom.value = '';
43826             }
43827             
43828             this.validate();
43829         }
43830     },
43831     
43832     getRawValue : function()
43833     {
43834         var v = this.inputEl().getValue();
43835         
43836         return v;
43837     },
43838     
43839     getValue : function()
43840     {
43841         return this.fixPrecision(this.parseValue(this.getRawValue()));
43842     },
43843     
43844     parseValue : function(value)
43845     {
43846         if(this.thousandsDelimiter) {
43847             value += "";
43848             r = new RegExp(",", "g");
43849             value = value.replace(r, "");
43850         }
43851         
43852         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43853         return isNaN(value) ? '' : value;
43854         
43855     },
43856     
43857     fixPrecision : function(value)
43858     {
43859         if(this.thousandsDelimiter) {
43860             value += "";
43861             r = new RegExp(",", "g");
43862             value = value.replace(r, "");
43863         }
43864         
43865         var nan = isNaN(value);
43866         
43867         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43868             return nan ? '' : value;
43869         }
43870         return parseFloat(value).toFixed(this.decimalPrecision);
43871     },
43872     
43873     decimalPrecisionFcn : function(v)
43874     {
43875         return Math.floor(v);
43876     },
43877     
43878     validateValue : function(value)
43879     {
43880         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43881             return false;
43882         }
43883         
43884         var num = this.parseValue(value);
43885         
43886         if(isNaN(num)){
43887             this.markInvalid(String.format(this.nanText, value));
43888             return false;
43889         }
43890         
43891         if(num < this.minValue){
43892             this.markInvalid(String.format(this.minText, this.minValue));
43893             return false;
43894         }
43895         
43896         if(num > this.maxValue){
43897             this.markInvalid(String.format(this.maxText, this.maxValue));
43898             return false;
43899         }
43900         
43901         return true;
43902     },
43903     
43904     validate : function()
43905     {
43906         if(this.disabled || this.allowBlank){
43907             this.markValid();
43908             return true;
43909         }
43910         
43911         var currency = this.getCurrency();
43912         
43913         if(this.validateValue(this.getRawValue()) && currency.length){
43914             this.markValid();
43915             return true;
43916         }
43917         
43918         this.markInvalid();
43919         return false;
43920     },
43921     
43922     getName: function()
43923     {
43924         return this.name;
43925     },
43926     
43927     beforeBlur : function()
43928     {
43929         if(!this.castInt){
43930             return;
43931         }
43932         
43933         var v = this.parseValue(this.getRawValue());
43934         
43935         if(v || v == 0){
43936             this.setValue(v);
43937         }
43938     },
43939     
43940     onBlur : function()
43941     {
43942         this.beforeBlur();
43943         
43944         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43945             //this.el.removeClass(this.focusClass);
43946         }
43947         
43948         this.hasFocus = false;
43949         
43950         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43951             this.validate();
43952         }
43953         
43954         var v = this.getValue();
43955         
43956         if(String(v) !== String(this.startValue)){
43957             this.fireEvent('change', this, v, this.startValue);
43958         }
43959         
43960         this.fireEvent("blur", this);
43961     },
43962     
43963     inputEl : function()
43964     {
43965         return this.el.select('.roo-money-amount-input', true).first();
43966     },
43967     
43968     currencyEl : function()
43969     {
43970         return this.el.select('.roo-money-currency-input', true).first();
43971     },
43972     
43973     hiddenEl : function()
43974     {
43975         return this.el.select('input.hidden-number-input',true).first();
43976     }
43977     
43978 });/**
43979  * @class Roo.bootstrap.BezierSignature
43980  * @extends Roo.bootstrap.Component
43981  * Bootstrap BezierSignature class
43982  * This script refer to:
43983  *    Title: Signature Pad
43984  *    Author: szimek
43985  *    Availability: https://github.com/szimek/signature_pad
43986  *
43987  * @constructor
43988  * Create a new BezierSignature
43989  * @param {Object} config The config object
43990  */
43991
43992 Roo.bootstrap.BezierSignature = function(config){
43993     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43994     this.addEvents({
43995         "resize" : true
43996     });
43997 };
43998
43999 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44000 {
44001      
44002     curve_data: [],
44003     
44004     is_empty: true,
44005     
44006     mouse_btn_down: true,
44007     
44008     /**
44009      * @cfg {int} canvas height
44010      */
44011     canvas_height: '200px',
44012     
44013     /**
44014      * @cfg {float|function} Radius of a single dot.
44015      */ 
44016     dot_size: false,
44017     
44018     /**
44019      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44020      */
44021     min_width: 0.5,
44022     
44023     /**
44024      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44025      */
44026     max_width: 2.5,
44027     
44028     /**
44029      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44030      */
44031     throttle: 16,
44032     
44033     /**
44034      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44035      */
44036     min_distance: 5,
44037     
44038     /**
44039      * @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.
44040      */
44041     bg_color: 'rgba(0, 0, 0, 0)',
44042     
44043     /**
44044      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44045      */
44046     dot_color: 'black',
44047     
44048     /**
44049      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44050      */ 
44051     velocity_filter_weight: 0.7,
44052     
44053     /**
44054      * @cfg {function} Callback when stroke begin. 
44055      */
44056     onBegin: false,
44057     
44058     /**
44059      * @cfg {function} Callback when stroke end.
44060      */
44061     onEnd: false,
44062     
44063     getAutoCreate : function()
44064     {
44065         var cls = 'roo-signature column';
44066         
44067         if(this.cls){
44068             cls += ' ' + this.cls;
44069         }
44070         
44071         var col_sizes = [
44072             'lg',
44073             'md',
44074             'sm',
44075             'xs'
44076         ];
44077         
44078         for(var i = 0; i < col_sizes.length; i++) {
44079             if(this[col_sizes[i]]) {
44080                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44081             }
44082         }
44083         
44084         var cfg = {
44085             tag: 'div',
44086             cls: cls,
44087             cn: [
44088                 {
44089                     tag: 'div',
44090                     cls: 'roo-signature-body',
44091                     cn: [
44092                         {
44093                             tag: 'canvas',
44094                             cls: 'roo-signature-body-canvas',
44095                             height: this.canvas_height,
44096                             width: this.canvas_width
44097                         }
44098                     ]
44099                 },
44100                 {
44101                     tag: 'input',
44102                     type: 'file',
44103                     style: 'display: none'
44104                 }
44105             ]
44106         };
44107         
44108         return cfg;
44109     },
44110     
44111     initEvents: function() 
44112     {
44113         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44114         
44115         var canvas = this.canvasEl();
44116         
44117         // mouse && touch event swapping...
44118         canvas.dom.style.touchAction = 'none';
44119         canvas.dom.style.msTouchAction = 'none';
44120         
44121         this.mouse_btn_down = false;
44122         canvas.on('mousedown', this._handleMouseDown, this);
44123         canvas.on('mousemove', this._handleMouseMove, this);
44124         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44125         
44126         if (window.PointerEvent) {
44127             canvas.on('pointerdown', this._handleMouseDown, this);
44128             canvas.on('pointermove', this._handleMouseMove, this);
44129             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44130         }
44131         
44132         if ('ontouchstart' in window) {
44133             canvas.on('touchstart', this._handleTouchStart, this);
44134             canvas.on('touchmove', this._handleTouchMove, this);
44135             canvas.on('touchend', this._handleTouchEnd, this);
44136         }
44137         
44138         Roo.EventManager.onWindowResize(this.resize, this, true);
44139         
44140         // file input event
44141         this.fileEl().on('change', this.uploadImage, this);
44142         
44143         this.clear();
44144         
44145         this.resize();
44146     },
44147     
44148     resize: function(){
44149         
44150         var canvas = this.canvasEl().dom;
44151         var ctx = this.canvasElCtx();
44152         var img_data = false;
44153         
44154         if(canvas.width > 0) {
44155             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44156         }
44157         // setting canvas width will clean img data
44158         canvas.width = 0;
44159         
44160         var style = window.getComputedStyle ? 
44161             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44162             
44163         var padding_left = parseInt(style.paddingLeft) || 0;
44164         var padding_right = parseInt(style.paddingRight) || 0;
44165         
44166         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44167         
44168         if(img_data) {
44169             ctx.putImageData(img_data, 0, 0);
44170         }
44171     },
44172     
44173     _handleMouseDown: function(e)
44174     {
44175         if (e.browserEvent.which === 1) {
44176             this.mouse_btn_down = true;
44177             this.strokeBegin(e);
44178         }
44179     },
44180     
44181     _handleMouseMove: function (e)
44182     {
44183         if (this.mouse_btn_down) {
44184             this.strokeMoveUpdate(e);
44185         }
44186     },
44187     
44188     _handleMouseUp: function (e)
44189     {
44190         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44191             this.mouse_btn_down = false;
44192             this.strokeEnd(e);
44193         }
44194     },
44195     
44196     _handleTouchStart: function (e) {
44197         
44198         e.preventDefault();
44199         if (e.browserEvent.targetTouches.length === 1) {
44200             // var touch = e.browserEvent.changedTouches[0];
44201             // this.strokeBegin(touch);
44202             
44203              this.strokeBegin(e); // assume e catching the correct xy...
44204         }
44205     },
44206     
44207     _handleTouchMove: function (e) {
44208         e.preventDefault();
44209         // var touch = event.targetTouches[0];
44210         // _this._strokeMoveUpdate(touch);
44211         this.strokeMoveUpdate(e);
44212     },
44213     
44214     _handleTouchEnd: function (e) {
44215         var wasCanvasTouched = e.target === this.canvasEl().dom;
44216         if (wasCanvasTouched) {
44217             e.preventDefault();
44218             // var touch = event.changedTouches[0];
44219             // _this._strokeEnd(touch);
44220             this.strokeEnd(e);
44221         }
44222     },
44223     
44224     reset: function () {
44225         this._lastPoints = [];
44226         this._lastVelocity = 0;
44227         this._lastWidth = (this.min_width + this.max_width) / 2;
44228         this.canvasElCtx().fillStyle = this.dot_color;
44229     },
44230     
44231     strokeMoveUpdate: function(e)
44232     {
44233         this.strokeUpdate(e);
44234         
44235         if (this.throttle) {
44236             this.throttleStroke(this.strokeUpdate, this.throttle);
44237         }
44238         else {
44239             this.strokeUpdate(e);
44240         }
44241     },
44242     
44243     strokeBegin: function(e)
44244     {
44245         var newPointGroup = {
44246             color: this.dot_color,
44247             points: []
44248         };
44249         
44250         if (typeof this.onBegin === 'function') {
44251             this.onBegin(e);
44252         }
44253         
44254         this.curve_data.push(newPointGroup);
44255         this.reset();
44256         this.strokeUpdate(e);
44257     },
44258     
44259     strokeUpdate: function(e)
44260     {
44261         var rect = this.canvasEl().dom.getBoundingClientRect();
44262         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44263         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44264         var lastPoints = lastPointGroup.points;
44265         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44266         var isLastPointTooClose = lastPoint
44267             ? point.distanceTo(lastPoint) <= this.min_distance
44268             : false;
44269         var color = lastPointGroup.color;
44270         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44271             var curve = this.addPoint(point);
44272             if (!lastPoint) {
44273                 this.drawDot({color: color, point: point});
44274             }
44275             else if (curve) {
44276                 this.drawCurve({color: color, curve: curve});
44277             }
44278             lastPoints.push({
44279                 time: point.time,
44280                 x: point.x,
44281                 y: point.y
44282             });
44283         }
44284     },
44285     
44286     strokeEnd: function(e)
44287     {
44288         this.strokeUpdate(e);
44289         if (typeof this.onEnd === 'function') {
44290             this.onEnd(e);
44291         }
44292     },
44293     
44294     addPoint:  function (point) {
44295         var _lastPoints = this._lastPoints;
44296         _lastPoints.push(point);
44297         if (_lastPoints.length > 2) {
44298             if (_lastPoints.length === 3) {
44299                 _lastPoints.unshift(_lastPoints[0]);
44300             }
44301             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44302             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44303             _lastPoints.shift();
44304             return curve;
44305         }
44306         return null;
44307     },
44308     
44309     calculateCurveWidths: function (startPoint, endPoint) {
44310         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44311             (1 - this.velocity_filter_weight) * this._lastVelocity;
44312
44313         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44314         var widths = {
44315             end: newWidth,
44316             start: this._lastWidth
44317         };
44318         
44319         this._lastVelocity = velocity;
44320         this._lastWidth = newWidth;
44321         return widths;
44322     },
44323     
44324     drawDot: function (_a) {
44325         var color = _a.color, point = _a.point;
44326         var ctx = this.canvasElCtx();
44327         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44328         ctx.beginPath();
44329         this.drawCurveSegment(point.x, point.y, width);
44330         ctx.closePath();
44331         ctx.fillStyle = color;
44332         ctx.fill();
44333     },
44334     
44335     drawCurve: function (_a) {
44336         var color = _a.color, curve = _a.curve;
44337         var ctx = this.canvasElCtx();
44338         var widthDelta = curve.endWidth - curve.startWidth;
44339         var drawSteps = Math.floor(curve.length()) * 2;
44340         ctx.beginPath();
44341         ctx.fillStyle = color;
44342         for (var i = 0; i < drawSteps; i += 1) {
44343         var t = i / drawSteps;
44344         var tt = t * t;
44345         var ttt = tt * t;
44346         var u = 1 - t;
44347         var uu = u * u;
44348         var uuu = uu * u;
44349         var x = uuu * curve.startPoint.x;
44350         x += 3 * uu * t * curve.control1.x;
44351         x += 3 * u * tt * curve.control2.x;
44352         x += ttt * curve.endPoint.x;
44353         var y = uuu * curve.startPoint.y;
44354         y += 3 * uu * t * curve.control1.y;
44355         y += 3 * u * tt * curve.control2.y;
44356         y += ttt * curve.endPoint.y;
44357         var width = curve.startWidth + ttt * widthDelta;
44358         this.drawCurveSegment(x, y, width);
44359         }
44360         ctx.closePath();
44361         ctx.fill();
44362     },
44363     
44364     drawCurveSegment: function (x, y, width) {
44365         var ctx = this.canvasElCtx();
44366         ctx.moveTo(x, y);
44367         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44368         this.is_empty = false;
44369     },
44370     
44371     clear: function()
44372     {
44373         var ctx = this.canvasElCtx();
44374         var canvas = this.canvasEl().dom;
44375         ctx.fillStyle = this.bg_color;
44376         ctx.clearRect(0, 0, canvas.width, canvas.height);
44377         ctx.fillRect(0, 0, canvas.width, canvas.height);
44378         this.curve_data = [];
44379         this.reset();
44380         this.is_empty = true;
44381     },
44382     
44383     fileEl: function()
44384     {
44385         return  this.el.select('input',true).first();
44386     },
44387     
44388     canvasEl: function()
44389     {
44390         return this.el.select('canvas',true).first();
44391     },
44392     
44393     canvasElCtx: function()
44394     {
44395         return this.el.select('canvas',true).first().dom.getContext('2d');
44396     },
44397     
44398     getImage: function(type)
44399     {
44400         if(this.is_empty) {
44401             return false;
44402         }
44403         
44404         // encryption ?
44405         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44406     },
44407     
44408     drawFromImage: function(img_src)
44409     {
44410         var img = new Image();
44411         
44412         img.onload = function(){
44413             this.canvasElCtx().drawImage(img, 0, 0);
44414         }.bind(this);
44415         
44416         img.src = img_src;
44417         
44418         this.is_empty = false;
44419     },
44420     
44421     selectImage: function()
44422     {
44423         this.fileEl().dom.click();
44424     },
44425     
44426     uploadImage: function(e)
44427     {
44428         var reader = new FileReader();
44429         
44430         reader.onload = function(e){
44431             var img = new Image();
44432             img.onload = function(){
44433                 this.reset();
44434                 this.canvasElCtx().drawImage(img, 0, 0);
44435             }.bind(this);
44436             img.src = e.target.result;
44437         }.bind(this);
44438         
44439         reader.readAsDataURL(e.target.files[0]);
44440     },
44441     
44442     // Bezier Point Constructor
44443     Point: (function () {
44444         function Point(x, y, time) {
44445             this.x = x;
44446             this.y = y;
44447             this.time = time || Date.now();
44448         }
44449         Point.prototype.distanceTo = function (start) {
44450             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44451         };
44452         Point.prototype.equals = function (other) {
44453             return this.x === other.x && this.y === other.y && this.time === other.time;
44454         };
44455         Point.prototype.velocityFrom = function (start) {
44456             return this.time !== start.time
44457             ? this.distanceTo(start) / (this.time - start.time)
44458             : 0;
44459         };
44460         return Point;
44461     }()),
44462     
44463     
44464     // Bezier Constructor
44465     Bezier: (function () {
44466         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44467             this.startPoint = startPoint;
44468             this.control2 = control2;
44469             this.control1 = control1;
44470             this.endPoint = endPoint;
44471             this.startWidth = startWidth;
44472             this.endWidth = endWidth;
44473         }
44474         Bezier.fromPoints = function (points, widths, scope) {
44475             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44476             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44477             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44478         };
44479         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44480             var dx1 = s1.x - s2.x;
44481             var dy1 = s1.y - s2.y;
44482             var dx2 = s2.x - s3.x;
44483             var dy2 = s2.y - s3.y;
44484             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44485             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44486             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44487             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44488             var dxm = m1.x - m2.x;
44489             var dym = m1.y - m2.y;
44490             var k = l2 / (l1 + l2);
44491             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44492             var tx = s2.x - cm.x;
44493             var ty = s2.y - cm.y;
44494             return {
44495                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44496                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44497             };
44498         };
44499         Bezier.prototype.length = function () {
44500             var steps = 10;
44501             var length = 0;
44502             var px;
44503             var py;
44504             for (var i = 0; i <= steps; i += 1) {
44505                 var t = i / steps;
44506                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44507                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44508                 if (i > 0) {
44509                     var xdiff = cx - px;
44510                     var ydiff = cy - py;
44511                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44512                 }
44513                 px = cx;
44514                 py = cy;
44515             }
44516             return length;
44517         };
44518         Bezier.prototype.point = function (t, start, c1, c2, end) {
44519             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44520             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44521             + (3.0 * c2 * (1.0 - t) * t * t)
44522             + (end * t * t * t);
44523         };
44524         return Bezier;
44525     }()),
44526     
44527     throttleStroke: function(fn, wait) {
44528       if (wait === void 0) { wait = 250; }
44529       var previous = 0;
44530       var timeout = null;
44531       var result;
44532       var storedContext;
44533       var storedArgs;
44534       var later = function () {
44535           previous = Date.now();
44536           timeout = null;
44537           result = fn.apply(storedContext, storedArgs);
44538           if (!timeout) {
44539               storedContext = null;
44540               storedArgs = [];
44541           }
44542       };
44543       return function wrapper() {
44544           var args = [];
44545           for (var _i = 0; _i < arguments.length; _i++) {
44546               args[_i] = arguments[_i];
44547           }
44548           var now = Date.now();
44549           var remaining = wait - (now - previous);
44550           storedContext = this;
44551           storedArgs = args;
44552           if (remaining <= 0 || remaining > wait) {
44553               if (timeout) {
44554                   clearTimeout(timeout);
44555                   timeout = null;
44556               }
44557               previous = now;
44558               result = fn.apply(storedContext, storedArgs);
44559               if (!timeout) {
44560                   storedContext = null;
44561                   storedArgs = [];
44562               }
44563           }
44564           else if (!timeout) {
44565               timeout = window.setTimeout(later, remaining);
44566           }
44567           return result;
44568       };
44569   }
44570   
44571 });
44572
44573  
44574
44575