Roo/bootstrap/CardUploader.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.headerContainerEl.dom.innerHTML = html;
2683     }
2684
2685     
2686 });
2687
2688 /*
2689  * - LGPL
2690  *
2691  * Card header - holder for the card header elements.
2692  * 
2693  */
2694
2695 /**
2696  * @class Roo.bootstrap.CardHeader
2697  * @extends Roo.bootstrap.Element
2698  * Bootstrap CardHeader class
2699  * @constructor
2700  * Create a new Card Header - that you can embed children into
2701  * @param {Object} config The config object
2702  */
2703
2704 Roo.bootstrap.CardHeader = function(config){
2705     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2706 };
2707
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2709     
2710     
2711     container_method : 'getCardHeader' 
2712     
2713      
2714     
2715     
2716    
2717 });
2718
2719  
2720
2721  /*
2722  * - LGPL
2723  *
2724  * Card footer - holder for the card footer elements.
2725  * 
2726  */
2727
2728 /**
2729  * @class Roo.bootstrap.CardFooter
2730  * @extends Roo.bootstrap.Element
2731  * Bootstrap CardFooter class
2732  * @constructor
2733  * Create a new Card Footer - that you can embed children into
2734  * @param {Object} config The config object
2735  */
2736
2737 Roo.bootstrap.CardFooter = function(config){
2738     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2739 };
2740
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2742     
2743     
2744     container_method : 'getCardFooter' 
2745     
2746      
2747     
2748     
2749    
2750 });
2751
2752  
2753
2754  /*
2755  * - LGPL
2756  *
2757  * Card header - holder for the card header elements.
2758  * 
2759  */
2760
2761 /**
2762  * @class Roo.bootstrap.CardImageTop
2763  * @extends Roo.bootstrap.Element
2764  * Bootstrap CardImageTop class
2765  * @constructor
2766  * Create a new Card Image Top container
2767  * @param {Object} config The config object
2768  */
2769
2770 Roo.bootstrap.CardImageTop = function(config){
2771     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2772 };
2773
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2775     
2776    
2777     container_method : 'getCardImageTop' 
2778     
2779      
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * image
2790  * 
2791  */
2792
2793
2794 /**
2795  * @class Roo.bootstrap.Img
2796  * @extends Roo.bootstrap.Component
2797  * Bootstrap Img class
2798  * @cfg {Boolean} imgResponsive false | true
2799  * @cfg {String} border rounded | circle | thumbnail
2800  * @cfg {String} src image source
2801  * @cfg {String} alt image alternative text
2802  * @cfg {String} href a tag href
2803  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804  * @cfg {String} xsUrl xs image source
2805  * @cfg {String} smUrl sm image source
2806  * @cfg {String} mdUrl md image source
2807  * @cfg {String} lgUrl lg image source
2808  * 
2809  * @constructor
2810  * Create a new Input
2811  * @param {Object} config The config object
2812  */
2813
2814 Roo.bootstrap.Img = function(config){
2815     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816     
2817     this.addEvents({
2818         // img events
2819         /**
2820          * @event click
2821          * The img click event for the img.
2822          * @param {Roo.EventObject} e
2823          */
2824         "click" : true
2825     });
2826 };
2827
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2829     
2830     imgResponsive: true,
2831     border: '',
2832     src: 'about:blank',
2833     href: false,
2834     target: false,
2835     xsUrl: '',
2836     smUrl: '',
2837     mdUrl: '',
2838     lgUrl: '',
2839
2840     getAutoCreate : function()
2841     {   
2842         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843             return this.createSingleImg();
2844         }
2845         
2846         var cfg = {
2847             tag: 'div',
2848             cls: 'roo-image-responsive-group',
2849             cn: []
2850         };
2851         var _this = this;
2852         
2853         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2854             
2855             if(!_this[size + 'Url']){
2856                 return;
2857             }
2858             
2859             var img = {
2860                 tag: 'img',
2861                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862                 html: _this.html || cfg.html,
2863                 src: _this[size + 'Url']
2864             };
2865             
2866             img.cls += ' roo-image-responsive-' + size;
2867             
2868             var s = ['xs', 'sm', 'md', 'lg'];
2869             
2870             s.splice(s.indexOf(size), 1);
2871             
2872             Roo.each(s, function(ss){
2873                 img.cls += ' hidden-' + ss;
2874             });
2875             
2876             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877                 cfg.cls += ' img-' + _this.border;
2878             }
2879             
2880             if(_this.alt){
2881                 cfg.alt = _this.alt;
2882             }
2883             
2884             if(_this.href){
2885                 var a = {
2886                     tag: 'a',
2887                     href: _this.href,
2888                     cn: [
2889                         img
2890                     ]
2891                 };
2892
2893                 if(this.target){
2894                     a.target = _this.target;
2895                 }
2896             }
2897             
2898             cfg.cn.push((_this.href) ? a : img);
2899             
2900         });
2901         
2902         return cfg;
2903     },
2904     
2905     createSingleImg : function()
2906     {
2907         var cfg = {
2908             tag: 'img',
2909             cls: (this.imgResponsive) ? 'img-responsive' : '',
2910             html : null,
2911             src : 'about:blank'  // just incase src get's set to undefined?!?
2912         };
2913         
2914         cfg.html = this.html || cfg.html;
2915         
2916         cfg.src = this.src || cfg.src;
2917         
2918         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919             cfg.cls += ' img-' + this.border;
2920         }
2921         
2922         if(this.alt){
2923             cfg.alt = this.alt;
2924         }
2925         
2926         if(this.href){
2927             var a = {
2928                 tag: 'a',
2929                 href: this.href,
2930                 cn: [
2931                     cfg
2932                 ]
2933             };
2934             
2935             if(this.target){
2936                 a.target = this.target;
2937             }
2938             
2939         }
2940         
2941         return (this.href) ? a : cfg;
2942     },
2943     
2944     initEvents: function() 
2945     {
2946         if(!this.href){
2947             this.el.on('click', this.onClick, this);
2948         }
2949         
2950     },
2951     
2952     onClick : function(e)
2953     {
2954         Roo.log('img onclick');
2955         this.fireEvent('click', this, e);
2956     },
2957     /**
2958      * Sets the url of the image - used to update it
2959      * @param {String} url the url of the image
2960      */
2961     
2962     setSrc : function(url)
2963     {
2964         this.src =  url;
2965         
2966         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967             this.el.dom.src =  url;
2968             return;
2969         }
2970         
2971         this.el.select('img', true).first().dom.src =  url;
2972     }
2973     
2974     
2975    
2976 });
2977
2978  /*
2979  * - LGPL
2980  *
2981  * image
2982  * 
2983  */
2984
2985
2986 /**
2987  * @class Roo.bootstrap.Link
2988  * @extends Roo.bootstrap.Component
2989  * Bootstrap Link Class
2990  * @cfg {String} alt image alternative text
2991  * @cfg {String} href a tag href
2992  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993  * @cfg {String} html the content of the link.
2994  * @cfg {String} anchor name for the anchor link
2995  * @cfg {String} fa - favicon
2996
2997  * @cfg {Boolean} preventDefault (true | false) default false
2998
2999  * 
3000  * @constructor
3001  * Create a new Input
3002  * @param {Object} config The config object
3003  */
3004
3005 Roo.bootstrap.Link = function(config){
3006     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007     
3008     this.addEvents({
3009         // img events
3010         /**
3011          * @event click
3012          * The img click event for the img.
3013          * @param {Roo.EventObject} e
3014          */
3015         "click" : true
3016     });
3017 };
3018
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3020     
3021     href: false,
3022     target: false,
3023     preventDefault: false,
3024     anchor : false,
3025     alt : false,
3026     fa: false,
3027
3028
3029     getAutoCreate : function()
3030     {
3031         var html = this.html || '';
3032         
3033         if (this.fa !== false) {
3034             html = '<i class="fa fa-' + this.fa + '"></i>';
3035         }
3036         var cfg = {
3037             tag: 'a'
3038         };
3039         // anchor's do not require html/href...
3040         if (this.anchor === false) {
3041             cfg.html = html;
3042             cfg.href = this.href || '#';
3043         } else {
3044             cfg.name = this.anchor;
3045             if (this.html !== false || this.fa !== false) {
3046                 cfg.html = html;
3047             }
3048             if (this.href !== false) {
3049                 cfg.href = this.href;
3050             }
3051         }
3052         
3053         if(this.alt !== false){
3054             cfg.alt = this.alt;
3055         }
3056         
3057         
3058         if(this.target !== false) {
3059             cfg.target = this.target;
3060         }
3061         
3062         return cfg;
3063     },
3064     
3065     initEvents: function() {
3066         
3067         if(!this.href || this.preventDefault){
3068             this.el.on('click', this.onClick, this);
3069         }
3070     },
3071     
3072     onClick : function(e)
3073     {
3074         if(this.preventDefault){
3075             e.preventDefault();
3076         }
3077         //Roo.log('img onclick');
3078         this.fireEvent('click', this, e);
3079     }
3080    
3081 });
3082
3083  /*
3084  * - LGPL
3085  *
3086  * header
3087  * 
3088  */
3089
3090 /**
3091  * @class Roo.bootstrap.Header
3092  * @extends Roo.bootstrap.Component
3093  * Bootstrap Header class
3094  * @cfg {String} html content of header
3095  * @cfg {Number} level (1|2|3|4|5|6) default 1
3096  * 
3097  * @constructor
3098  * Create a new Header
3099  * @param {Object} config The config object
3100  */
3101
3102
3103 Roo.bootstrap.Header  = function(config){
3104     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3105 };
3106
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3108     
3109     //href : false,
3110     html : false,
3111     level : 1,
3112     
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         
3119         var cfg = {
3120             tag: 'h' + (1 *this.level),
3121             html: this.html || ''
3122         } ;
3123         
3124         return cfg;
3125     }
3126    
3127 });
3128
3129  
3130
3131  /*
3132  * Based on:
3133  * Ext JS Library 1.1.1
3134  * Copyright(c) 2006-2007, Ext JS, LLC.
3135  *
3136  * Originally Released Under LGPL - original licence link has changed is not relivant.
3137  *
3138  * Fork - LGPL
3139  * <script type="text/javascript">
3140  */
3141  
3142 /**
3143  * @class Roo.bootstrap.MenuMgr
3144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3145  * @singleton
3146  */
3147 Roo.bootstrap.MenuMgr = function(){
3148    var menus, active, groups = {}, attached = false, lastShow = new Date();
3149
3150    // private - called when first menu is created
3151    function init(){
3152        menus = {};
3153        active = new Roo.util.MixedCollection();
3154        Roo.get(document).addKeyListener(27, function(){
3155            if(active.length > 0){
3156                hideAll();
3157            }
3158        });
3159    }
3160
3161    // private
3162    function hideAll(){
3163        if(active && active.length > 0){
3164            var c = active.clone();
3165            c.each(function(m){
3166                m.hide();
3167            });
3168        }
3169    }
3170
3171    // private
3172    function onHide(m){
3173        active.remove(m);
3174        if(active.length < 1){
3175            Roo.get(document).un("mouseup", onMouseDown);
3176             
3177            attached = false;
3178        }
3179    }
3180
3181    // private
3182    function onShow(m){
3183        var last = active.last();
3184        lastShow = new Date();
3185        active.add(m);
3186        if(!attached){
3187           Roo.get(document).on("mouseup", onMouseDown);
3188            
3189            attached = true;
3190        }
3191        if(m.parentMenu){
3192           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193           m.parentMenu.activeChild = m;
3194        }else if(last && last.isVisible()){
3195           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3196        }
3197    }
3198
3199    // private
3200    function onBeforeHide(m){
3201        if(m.activeChild){
3202            m.activeChild.hide();
3203        }
3204        if(m.autoHideTimer){
3205            clearTimeout(m.autoHideTimer);
3206            delete m.autoHideTimer;
3207        }
3208    }
3209
3210    // private
3211    function onBeforeShow(m){
3212        var pm = m.parentMenu;
3213        if(!pm && !m.allowOtherMenus){
3214            hideAll();
3215        }else if(pm && pm.activeChild && active != m){
3216            pm.activeChild.hide();
3217        }
3218    }
3219
3220    // private this should really trigger on mouseup..
3221    function onMouseDown(e){
3222         Roo.log("on Mouse Up");
3223         
3224         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225             Roo.log("MenuManager hideAll");
3226             hideAll();
3227             e.stopEvent();
3228         }
3229         
3230         
3231    }
3232
3233    // private
3234    function onBeforeCheck(mi, state){
3235        if(state){
3236            var g = groups[mi.group];
3237            for(var i = 0, l = g.length; i < l; i++){
3238                if(g[i] != mi){
3239                    g[i].setChecked(false);
3240                }
3241            }
3242        }
3243    }
3244
3245    return {
3246
3247        /**
3248         * Hides all menus that are currently visible
3249         */
3250        hideAll : function(){
3251             hideAll();  
3252        },
3253
3254        // private
3255        register : function(menu){
3256            if(!menus){
3257                init();
3258            }
3259            menus[menu.id] = menu;
3260            menu.on("beforehide", onBeforeHide);
3261            menu.on("hide", onHide);
3262            menu.on("beforeshow", onBeforeShow);
3263            menu.on("show", onShow);
3264            var g = menu.group;
3265            if(g && menu.events["checkchange"]){
3266                if(!groups[g]){
3267                    groups[g] = [];
3268                }
3269                groups[g].push(menu);
3270                menu.on("checkchange", onCheck);
3271            }
3272        },
3273
3274         /**
3275          * Returns a {@link Roo.menu.Menu} object
3276          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277          * be used to generate and return a new Menu instance.
3278          */
3279        get : function(menu){
3280            if(typeof menu == "string"){ // menu id
3281                return menus[menu];
3282            }else if(menu.events){  // menu instance
3283                return menu;
3284            }
3285            /*else if(typeof menu.length == 'number'){ // array of menu items?
3286                return new Roo.bootstrap.Menu({items:menu});
3287            }else{ // otherwise, must be a config
3288                return new Roo.bootstrap.Menu(menu);
3289            }
3290            */
3291            return false;
3292        },
3293
3294        // private
3295        unregister : function(menu){
3296            delete menus[menu.id];
3297            menu.un("beforehide", onBeforeHide);
3298            menu.un("hide", onHide);
3299            menu.un("beforeshow", onBeforeShow);
3300            menu.un("show", onShow);
3301            var g = menu.group;
3302            if(g && menu.events["checkchange"]){
3303                groups[g].remove(menu);
3304                menu.un("checkchange", onCheck);
3305            }
3306        },
3307
3308        // private
3309        registerCheckable : function(menuItem){
3310            var g = menuItem.group;
3311            if(g){
3312                if(!groups[g]){
3313                    groups[g] = [];
3314                }
3315                groups[g].push(menuItem);
3316                menuItem.on("beforecheckchange", onBeforeCheck);
3317            }
3318        },
3319
3320        // private
3321        unregisterCheckable : function(menuItem){
3322            var g = menuItem.group;
3323            if(g){
3324                groups[g].remove(menuItem);
3325                menuItem.un("beforecheckchange", onBeforeCheck);
3326            }
3327        }
3328    };
3329 }();/*
3330  * - LGPL
3331  *
3332  * menu
3333  * 
3334  */
3335
3336 /**
3337  * @class Roo.bootstrap.Menu
3338  * @extends Roo.bootstrap.Component
3339  * Bootstrap Menu class - container for MenuItems
3340  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3342  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3343  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3344  * 
3345  * @constructor
3346  * Create a new Menu
3347  * @param {Object} config The config object
3348  */
3349
3350
3351 Roo.bootstrap.Menu = function(config){
3352     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353     if (this.registerMenu && this.type != 'treeview')  {
3354         Roo.bootstrap.MenuMgr.register(this);
3355     }
3356     
3357     
3358     this.addEvents({
3359         /**
3360          * @event beforeshow
3361          * Fires before this menu is displayed (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforeshow : true,
3365         /**
3366          * @event beforehide
3367          * Fires before this menu is hidden (return false to block)
3368          * @param {Roo.menu.Menu} this
3369          */
3370         beforehide : true,
3371         /**
3372          * @event show
3373          * Fires after this menu is displayed
3374          * @param {Roo.menu.Menu} this
3375          */
3376         show : true,
3377         /**
3378          * @event hide
3379          * Fires after this menu is hidden
3380          * @param {Roo.menu.Menu} this
3381          */
3382         hide : true,
3383         /**
3384          * @event click
3385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386          * @param {Roo.menu.Menu} this
3387          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388          * @param {Roo.EventObject} e
3389          */
3390         click : true,
3391         /**
3392          * @event mouseover
3393          * Fires when the mouse is hovering over this menu
3394          * @param {Roo.menu.Menu} this
3395          * @param {Roo.EventObject} e
3396          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3397          */
3398         mouseover : true,
3399         /**
3400          * @event mouseout
3401          * Fires when the mouse exits this menu
3402          * @param {Roo.menu.Menu} this
3403          * @param {Roo.EventObject} e
3404          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3405          */
3406         mouseout : true,
3407         /**
3408          * @event itemclick
3409          * Fires when a menu item contained in this menu is clicked
3410          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411          * @param {Roo.EventObject} e
3412          */
3413         itemclick: true
3414     });
3415     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3416 };
3417
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3419     
3420    /// html : false,
3421     //align : '',
3422     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3423     type: false,
3424     /**
3425      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3426      */
3427     registerMenu : true,
3428     
3429     menuItems :false, // stores the menu items..
3430     
3431     hidden:true,
3432         
3433     parentMenu : false,
3434     
3435     stopEvent : true,
3436     
3437     isLink : false,
3438     
3439     getChildContainer : function() {
3440         return this.el;  
3441     },
3442     
3443     getAutoCreate : function(){
3444          
3445         //if (['right'].indexOf(this.align)!==-1) {
3446         //    cfg.cn[1].cls += ' pull-right'
3447         //}
3448         
3449         
3450         var cfg = {
3451             tag : 'ul',
3452             cls : 'dropdown-menu' ,
3453             style : 'z-index:1000'
3454             
3455         };
3456         
3457         if (this.type === 'submenu') {
3458             cfg.cls = 'submenu active';
3459         }
3460         if (this.type === 'treeview') {
3461             cfg.cls = 'treeview-menu';
3462         }
3463         
3464         return cfg;
3465     },
3466     initEvents : function() {
3467         
3468        // Roo.log("ADD event");
3469        // Roo.log(this.triggerEl.dom);
3470         
3471         this.triggerEl.on('click', this.onTriggerClick, this);
3472         
3473         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3474         
3475         
3476         if (this.triggerEl.hasClass('nav-item')) {
3477             // dropdown toggle on the 'a' in BS4?
3478             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3479         } else {
3480             this.triggerEl.addClass('dropdown-toggle');
3481         }
3482         if (Roo.isTouch) {
3483             this.el.on('touchstart'  , this.onTouch, this);
3484         }
3485         this.el.on('click' , this.onClick, this);
3486
3487         this.el.on("mouseover", this.onMouseOver, this);
3488         this.el.on("mouseout", this.onMouseOut, this);
3489         
3490     },
3491     
3492     findTargetItem : function(e)
3493     {
3494         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3495         if(!t){
3496             return false;
3497         }
3498         //Roo.log(t);         Roo.log(t.id);
3499         if(t && t.id){
3500             //Roo.log(this.menuitems);
3501             return this.menuitems.get(t.id);
3502             
3503             //return this.items.get(t.menuItemId);
3504         }
3505         
3506         return false;
3507     },
3508     
3509     onTouch : function(e) 
3510     {
3511         Roo.log("menu.onTouch");
3512         //e.stopEvent(); this make the user popdown broken
3513         this.onClick(e);
3514     },
3515     
3516     onClick : function(e)
3517     {
3518         Roo.log("menu.onClick");
3519         
3520         var t = this.findTargetItem(e);
3521         if(!t || t.isContainer){
3522             return;
3523         }
3524         Roo.log(e);
3525         /*
3526         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3527             if(t == this.activeItem && t.shouldDeactivate(e)){
3528                 this.activeItem.deactivate();
3529                 delete this.activeItem;
3530                 return;
3531             }
3532             if(t.canActivate){
3533                 this.setActiveItem(t, true);
3534             }
3535             return;
3536             
3537             
3538         }
3539         */
3540        
3541         Roo.log('pass click event');
3542         
3543         t.onClick(e);
3544         
3545         this.fireEvent("click", this, t, e);
3546         
3547         var _this = this;
3548         
3549         if(!t.href.length || t.href == '#'){
3550             (function() { _this.hide(); }).defer(100);
3551         }
3552         
3553     },
3554     
3555     onMouseOver : function(e){
3556         var t  = this.findTargetItem(e);
3557         //Roo.log(t);
3558         //if(t){
3559         //    if(t.canActivate && !t.disabled){
3560         //        this.setActiveItem(t, true);
3561         //    }
3562         //}
3563         
3564         this.fireEvent("mouseover", this, e, t);
3565     },
3566     isVisible : function(){
3567         return !this.hidden;
3568     },
3569     onMouseOut : function(e){
3570         var t  = this.findTargetItem(e);
3571         
3572         //if(t ){
3573         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3574         //        this.activeItem.deactivate();
3575         //        delete this.activeItem;
3576         //    }
3577         //}
3578         this.fireEvent("mouseout", this, e, t);
3579     },
3580     
3581     
3582     /**
3583      * Displays this menu relative to another element
3584      * @param {String/HTMLElement/Roo.Element} element The element to align to
3585      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586      * the element (defaults to this.defaultAlign)
3587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3588      */
3589     show : function(el, pos, parentMenu)
3590     {
3591         if (false === this.fireEvent("beforeshow", this)) {
3592             Roo.log("show canceled");
3593             return;
3594         }
3595         this.parentMenu = parentMenu;
3596         if(!this.el){
3597             this.render();
3598         }
3599         
3600         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3601     },
3602      /**
3603      * Displays this menu at a specific xy position
3604      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3606      */
3607     showAt : function(xy, parentMenu, /* private: */_e){
3608         this.parentMenu = parentMenu;
3609         if(!this.el){
3610             this.render();
3611         }
3612         if(_e !== false){
3613             this.fireEvent("beforeshow", this);
3614             //xy = this.el.adjustForConstraints(xy);
3615         }
3616         
3617         //this.el.show();
3618         this.hideMenuItems();
3619         this.hidden = false;
3620         this.triggerEl.addClass('open');
3621         this.el.addClass('show');
3622         
3623         // reassign x when hitting right
3624         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3626         }
3627         
3628         // reassign y when hitting bottom
3629         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3631         }
3632         
3633         // but the list may align on trigger left or trigger top... should it be a properity?
3634         
3635         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3636             this.el.setXY(xy);
3637         }
3638         
3639         this.focus();
3640         this.fireEvent("show", this);
3641     },
3642     
3643     focus : function(){
3644         return;
3645         if(!this.hidden){
3646             this.doFocus.defer(50, this);
3647         }
3648     },
3649
3650     doFocus : function(){
3651         if(!this.hidden){
3652             this.focusEl.focus();
3653         }
3654     },
3655
3656     /**
3657      * Hides this menu and optionally all parent menus
3658      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3659      */
3660     hide : function(deep)
3661     {
3662         if (false === this.fireEvent("beforehide", this)) {
3663             Roo.log("hide canceled");
3664             return;
3665         }
3666         this.hideMenuItems();
3667         if(this.el && this.isVisible()){
3668            
3669             if(this.activeItem){
3670                 this.activeItem.deactivate();
3671                 this.activeItem = null;
3672             }
3673             this.triggerEl.removeClass('open');;
3674             this.el.removeClass('show');
3675             this.hidden = true;
3676             this.fireEvent("hide", this);
3677         }
3678         if(deep === true && this.parentMenu){
3679             this.parentMenu.hide(true);
3680         }
3681     },
3682     
3683     onTriggerClick : function(e)
3684     {
3685         Roo.log('trigger click');
3686         
3687         var target = e.getTarget();
3688         
3689         Roo.log(target.nodeName.toLowerCase());
3690         
3691         if(target.nodeName.toLowerCase() === 'i'){
3692             e.preventDefault();
3693         }
3694         
3695     },
3696     
3697     onTriggerPress  : function(e)
3698     {
3699         Roo.log('trigger press');
3700         //Roo.log(e.getTarget());
3701        // Roo.log(this.triggerEl.dom);
3702        
3703         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704         var pel = Roo.get(e.getTarget());
3705         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706             Roo.log('is treeview or dropdown?');
3707             return;
3708         }
3709         
3710         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3711             return;
3712         }
3713         
3714         if (this.isVisible()) {
3715             Roo.log('hide');
3716             this.hide();
3717         } else {
3718             Roo.log('show');
3719             this.show(this.triggerEl, '?', false);
3720         }
3721         
3722         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723             e.stopEvent();
3724         }
3725         
3726     },
3727        
3728     
3729     hideMenuItems : function()
3730     {
3731         Roo.log("hide Menu Items");
3732         if (!this.el) { 
3733             return;
3734         }
3735         
3736         this.el.select('.open',true).each(function(aa) {
3737             
3738             aa.removeClass('open');
3739          
3740         });
3741     },
3742     addxtypeChild : function (tree, cntr) {
3743         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3744           
3745         this.menuitems.add(comp);
3746         return comp;
3747
3748     },
3749     getEl : function()
3750     {
3751         Roo.log(this.el);
3752         return this.el;
3753     },
3754     
3755     clear : function()
3756     {
3757         this.getEl().dom.innerHTML = '';
3758         this.menuitems.clear();
3759     }
3760 });
3761
3762  
3763  /*
3764  * - LGPL
3765  *
3766  * menu item
3767  * 
3768  */
3769
3770
3771 /**
3772  * @class Roo.bootstrap.MenuItem
3773  * @extends Roo.bootstrap.Component
3774  * Bootstrap MenuItem class
3775  * @cfg {String} html the menu label
3776  * @cfg {String} href the link
3777  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3780  * @cfg {String} fa favicon to show on left of menu item.
3781  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new MenuItem
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.MenuItem = function(config){
3791     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3792     this.addEvents({
3793         // raw events
3794         /**
3795          * @event click
3796          * The raw click event for the entire grid.
3797          * @param {Roo.bootstrap.MenuItem} this
3798          * @param {Roo.EventObject} e
3799          */
3800         "click" : true
3801     });
3802 };
3803
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3805     
3806     href : false,
3807     html : false,
3808     preventDefault: false,
3809     isContainer : false,
3810     active : false,
3811     fa: false,
3812     
3813     getAutoCreate : function(){
3814         
3815         if(this.isContainer){
3816             return {
3817                 tag: 'li',
3818                 cls: 'dropdown-menu-item '
3819             };
3820         }
3821         var ctag = {
3822             tag: 'span',
3823             html: 'Link'
3824         };
3825         
3826         var anc = {
3827             tag : 'a',
3828             cls : 'dropdown-item',
3829             href : '#',
3830             cn : [  ]
3831         };
3832         
3833         if (this.fa !== false) {
3834             anc.cn.push({
3835                 tag : 'i',
3836                 cls : 'fa fa-' + this.fa
3837             });
3838         }
3839         
3840         anc.cn.push(ctag);
3841         
3842         
3843         var cfg= {
3844             tag: 'li',
3845             cls: 'dropdown-menu-item',
3846             cn: [ anc ]
3847         };
3848         if (this.parent().type == 'treeview') {
3849             cfg.cls = 'treeview-menu';
3850         }
3851         if (this.active) {
3852             cfg.cls += ' active';
3853         }
3854         
3855         
3856         
3857         anc.href = this.href || cfg.cn[0].href ;
3858         ctag.html = this.html || cfg.cn[0].html ;
3859         return cfg;
3860     },
3861     
3862     initEvents: function()
3863     {
3864         if (this.parent().type == 'treeview') {
3865             this.el.select('a').on('click', this.onClick, this);
3866         }
3867         
3868         if (this.menu) {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874     },
3875     onClick : function(e)
3876     {
3877         Roo.log('item on click ');
3878         
3879         if(this.preventDefault){
3880             e.preventDefault();
3881         }
3882         //this.parent().hideMenuItems();
3883         
3884         this.fireEvent('click', this, e);
3885     },
3886     getEl : function()
3887     {
3888         return this.el;
3889     } 
3890 });
3891
3892  
3893
3894  /*
3895  * - LGPL
3896  *
3897  * menu separator
3898  * 
3899  */
3900
3901
3902 /**
3903  * @class Roo.bootstrap.MenuSeparator
3904  * @extends Roo.bootstrap.Component
3905  * Bootstrap MenuSeparator class
3906  * 
3907  * @constructor
3908  * Create a new MenuItem
3909  * @param {Object} config The config object
3910  */
3911
3912
3913 Roo.bootstrap.MenuSeparator = function(config){
3914     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3915 };
3916
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3918     
3919     getAutoCreate : function(){
3920         var cfg = {
3921             cls: 'divider',
3922             tag : 'li'
3923         };
3924         
3925         return cfg;
3926     }
3927    
3928 });
3929
3930  
3931
3932  
3933 /*
3934 * Licence: LGPL
3935 */
3936
3937 /**
3938  * @class Roo.bootstrap.Modal
3939  * @extends Roo.bootstrap.Component
3940  * Bootstrap Modal class
3941  * @cfg {String} title Title of dialog
3942  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3944  * @cfg {Boolean} specificTitle default false
3945  * @cfg {Array} buttons Array of buttons or standard button set..
3946  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947  * @cfg {Boolean} animate default true
3948  * @cfg {Boolean} allow_close default true
3949  * @cfg {Boolean} fitwindow default false
3950  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3951  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3952  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3953  * @cfg {String} size (sm|lg|xl) default empty
3954  * @cfg {Number} max_width set the max width of modal
3955  * @cfg {Boolean} editableTitle can the title be edited
3956
3957  *
3958  *
3959  * @constructor
3960  * Create a new Modal Dialog
3961  * @param {Object} config The config object
3962  */
3963
3964 Roo.bootstrap.Modal = function(config){
3965     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3966     this.addEvents({
3967         // raw events
3968         /**
3969          * @event btnclick
3970          * The raw btnclick event for the button
3971          * @param {Roo.EventObject} e
3972          */
3973         "btnclick" : true,
3974         /**
3975          * @event resize
3976          * Fire when dialog resize
3977          * @param {Roo.bootstrap.Modal} this
3978          * @param {Roo.EventObject} e
3979          */
3980         "resize" : true,
3981         /**
3982          * @event titlechanged
3983          * Fire when the editable title has been changed
3984          * @param {Roo.bootstrap.Modal} this
3985          * @param {Roo.EventObject} value
3986          */
3987         "titlechanged" : true 
3988         
3989     });
3990     this.buttons = this.buttons || [];
3991
3992     if (this.tmpl) {
3993         this.tmpl = Roo.factory(this.tmpl);
3994     }
3995
3996 };
3997
3998 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3999
4000     title : 'test dialog',
4001
4002     buttons : false,
4003
4004     // set on load...
4005
4006     html: false,
4007
4008     tmp: false,
4009
4010     specificTitle: false,
4011
4012     buttonPosition: 'right',
4013
4014     allow_close : true,
4015
4016     animate : true,
4017
4018     fitwindow: false,
4019     
4020      // private
4021     dialogEl: false,
4022     bodyEl:  false,
4023     footerEl:  false,
4024     titleEl:  false,
4025     closeEl:  false,
4026
4027     size: '',
4028     
4029     max_width: 0,
4030     
4031     max_height: 0,
4032     
4033     fit_content: false,
4034     editableTitle  : false,
4035
4036     onRender : function(ct, position)
4037     {
4038         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4039
4040         if(!this.el){
4041             var cfg = Roo.apply({},  this.getAutoCreate());
4042             cfg.id = Roo.id();
4043             //if(!cfg.name){
4044             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4045             //}
4046             //if (!cfg.name.length) {
4047             //    delete cfg.name;
4048            // }
4049             if (this.cls) {
4050                 cfg.cls += ' ' + this.cls;
4051             }
4052             if (this.style) {
4053                 cfg.style = this.style;
4054             }
4055             this.el = Roo.get(document.body).createChild(cfg, position);
4056         }
4057         //var type = this.el.dom.type;
4058
4059
4060         if(this.tabIndex !== undefined){
4061             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4062         }
4063
4064         this.dialogEl = this.el.select('.modal-dialog',true).first();
4065         this.bodyEl = this.el.select('.modal-body',true).first();
4066         this.closeEl = this.el.select('.modal-header .close', true).first();
4067         this.headerEl = this.el.select('.modal-header',true).first();
4068         this.titleEl = this.el.select('.modal-title',true).first();
4069         this.footerEl = this.el.select('.modal-footer',true).first();
4070
4071         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4072         
4073         //this.el.addClass("x-dlg-modal");
4074
4075         if (this.buttons.length) {
4076             Roo.each(this.buttons, function(bb) {
4077                 var b = Roo.apply({}, bb);
4078                 b.xns = b.xns || Roo.bootstrap;
4079                 b.xtype = b.xtype || 'Button';
4080                 if (typeof(b.listeners) == 'undefined') {
4081                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4082                 }
4083
4084                 var btn = Roo.factory(b);
4085
4086                 btn.render(this.getButtonContainer());
4087
4088             },this);
4089         }
4090         // render the children.
4091         var nitems = [];
4092
4093         if(typeof(this.items) != 'undefined'){
4094             var items = this.items;
4095             delete this.items;
4096
4097             for(var i =0;i < items.length;i++) {
4098                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4099             }
4100         }
4101
4102         this.items = nitems;
4103
4104         // where are these used - they used to be body/close/footer
4105
4106
4107         this.initEvents();
4108         //this.el.addClass([this.fieldClass, this.cls]);
4109
4110     },
4111
4112     getAutoCreate : function()
4113     {
4114         // we will default to modal-body-overflow - might need to remove or make optional later.
4115         var bdy = {
4116                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4117                 html : this.html || ''
4118         };
4119
4120         var title = {
4121             tag: 'h5',
4122             cls : 'modal-title',
4123             html : this.title
4124         };
4125
4126         if(this.specificTitle){ // WTF is this?
4127             title = this.title;
4128         }
4129
4130         var header = [];
4131         if (this.allow_close && Roo.bootstrap.version == 3) {
4132             header.push({
4133                 tag: 'button',
4134                 cls : 'close',
4135                 html : '&times'
4136             });
4137         }
4138
4139         header.push(title);
4140
4141         if (this.editableTitle) {
4142             header.push({
4143                 cls: 'form-control roo-editable-title d-none',
4144                 tag: 'input',
4145                 type: 'text'
4146             });
4147         }
4148         
4149         if (this.allow_close && Roo.bootstrap.version == 4) {
4150             header.push({
4151                 tag: 'button',
4152                 cls : 'close',
4153                 html : '&times'
4154             });
4155         }
4156         
4157         var size = '';
4158
4159         if(this.size.length){
4160             size = 'modal-' + this.size;
4161         }
4162         
4163         var footer = Roo.bootstrap.version == 3 ?
4164             {
4165                 cls : 'modal-footer',
4166                 cn : [
4167                     {
4168                         tag: 'div',
4169                         cls: 'btn-' + this.buttonPosition
4170                     }
4171                 ]
4172
4173             } :
4174             {  // BS4 uses mr-auto on left buttons....
4175                 cls : 'modal-footer'
4176             };
4177
4178             
4179
4180         
4181         
4182         var modal = {
4183             cls: "modal",
4184              cn : [
4185                 {
4186                     cls: "modal-dialog " + size,
4187                     cn : [
4188                         {
4189                             cls : "modal-content",
4190                             cn : [
4191                                 {
4192                                     cls : 'modal-header',
4193                                     cn : header
4194                                 },
4195                                 bdy,
4196                                 footer
4197                             ]
4198
4199                         }
4200                     ]
4201
4202                 }
4203             ]
4204         };
4205
4206         if(this.animate){
4207             modal.cls += ' fade';
4208         }
4209
4210         return modal;
4211
4212     },
4213     getChildContainer : function() {
4214
4215          return this.bodyEl;
4216
4217     },
4218     getButtonContainer : function() {
4219         
4220          return Roo.bootstrap.version == 4 ?
4221             this.el.select('.modal-footer',true).first()
4222             : this.el.select('.modal-footer div',true).first();
4223
4224     },
4225     initEvents : function()
4226     {
4227         if (this.allow_close) {
4228             this.closeEl.on('click', this.hide, this);
4229         }
4230         Roo.EventManager.onWindowResize(this.resize, this, true);
4231         if (this.editableTitle) {
4232             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4233             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4234             this.headerEditEl.on('keyup', function(e) {
4235                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4236                         this.toggleHeaderInput(false)
4237                     }
4238                 }, this);
4239             this.headerEditEl.on('blur', function(e) {
4240                 this.toggleHeaderInput(false)
4241             },this);
4242         }
4243
4244     },
4245   
4246
4247     resize : function()
4248     {
4249         this.maskEl.setSize(
4250             Roo.lib.Dom.getViewWidth(true),
4251             Roo.lib.Dom.getViewHeight(true)
4252         );
4253         
4254         if (this.fitwindow) {
4255             
4256            this.dialogEl.setStyle( { 'max-width' : '100%' });
4257             this.setSize(
4258                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4259                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4260             );
4261             return;
4262         }
4263         
4264         if(this.max_width !== 0) {
4265             
4266             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4267             
4268             if(this.height) {
4269                 this.setSize(w, this.height);
4270                 return;
4271             }
4272             
4273             if(this.max_height) {
4274                 this.setSize(w,Math.min(
4275                     this.max_height,
4276                     Roo.lib.Dom.getViewportHeight(true) - 60
4277                 ));
4278                 
4279                 return;
4280             }
4281             
4282             if(!this.fit_content) {
4283                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4284                 return;
4285             }
4286             
4287             this.setSize(w, Math.min(
4288                 60 +
4289                 this.headerEl.getHeight() + 
4290                 this.footerEl.getHeight() + 
4291                 this.getChildHeight(this.bodyEl.dom.childNodes),
4292                 Roo.lib.Dom.getViewportHeight(true) - 60)
4293             );
4294         }
4295         
4296     },
4297
4298     setSize : function(w,h)
4299     {
4300         if (!w && !h) {
4301             return;
4302         }
4303         
4304         this.resizeTo(w,h);
4305     },
4306
4307     show : function() {
4308
4309         if (!this.rendered) {
4310             this.render();
4311         }
4312         this.toggleHeaderInput(false);
4313         //this.el.setStyle('display', 'block');
4314         this.el.removeClass('hideing');
4315         this.el.dom.style.display='block';
4316         
4317         Roo.get(document.body).addClass('modal-open');
4318  
4319         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4320             
4321             (function(){
4322                 this.el.addClass('show');
4323                 this.el.addClass('in');
4324             }).defer(50, this);
4325         }else{
4326             this.el.addClass('show');
4327             this.el.addClass('in');
4328         }
4329
4330         // not sure how we can show data in here..
4331         //if (this.tmpl) {
4332         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4333         //}
4334
4335         Roo.get(document.body).addClass("x-body-masked");
4336         
4337         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4338         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4339         this.maskEl.dom.style.display = 'block';
4340         this.maskEl.addClass('show');
4341         
4342         
4343         this.resize();
4344         
4345         this.fireEvent('show', this);
4346
4347         // set zindex here - otherwise it appears to be ignored...
4348         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4349
4350         (function () {
4351             this.items.forEach( function(e) {
4352                 e.layout ? e.layout() : false;
4353
4354             });
4355         }).defer(100,this);
4356
4357     },
4358     hide : function()
4359     {
4360         if(this.fireEvent("beforehide", this) !== false){
4361             
4362             this.maskEl.removeClass('show');
4363             
4364             this.maskEl.dom.style.display = '';
4365             Roo.get(document.body).removeClass("x-body-masked");
4366             this.el.removeClass('in');
4367             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4368
4369             if(this.animate){ // why
4370                 this.el.addClass('hideing');
4371                 this.el.removeClass('show');
4372                 (function(){
4373                     if (!this.el.hasClass('hideing')) {
4374                         return; // it's been shown again...
4375                     }
4376                     
4377                     this.el.dom.style.display='';
4378
4379                     Roo.get(document.body).removeClass('modal-open');
4380                     this.el.removeClass('hideing');
4381                 }).defer(150,this);
4382                 
4383             }else{
4384                 this.el.removeClass('show');
4385                 this.el.dom.style.display='';
4386                 Roo.get(document.body).removeClass('modal-open');
4387
4388             }
4389             this.fireEvent('hide', this);
4390         }
4391     },
4392     isVisible : function()
4393     {
4394         
4395         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4396         
4397     },
4398
4399     addButton : function(str, cb)
4400     {
4401
4402
4403         var b = Roo.apply({}, { html : str } );
4404         b.xns = b.xns || Roo.bootstrap;
4405         b.xtype = b.xtype || 'Button';
4406         if (typeof(b.listeners) == 'undefined') {
4407             b.listeners = { click : cb.createDelegate(this)  };
4408         }
4409
4410         var btn = Roo.factory(b);
4411
4412         btn.render(this.getButtonContainer());
4413
4414         return btn;
4415
4416     },
4417
4418     setDefaultButton : function(btn)
4419     {
4420         //this.el.select('.modal-footer').()
4421     },
4422
4423     resizeTo: function(w,h)
4424     {
4425         this.dialogEl.setWidth(w);
4426         
4427         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4428
4429         this.bodyEl.setHeight(h - diff);
4430         
4431         this.fireEvent('resize', this);
4432     },
4433     
4434     setContentSize  : function(w, h)
4435     {
4436
4437     },
4438     onButtonClick: function(btn,e)
4439     {
4440         //Roo.log([a,b,c]);
4441         this.fireEvent('btnclick', btn.name, e);
4442     },
4443      /**
4444      * Set the title of the Dialog
4445      * @param {String} str new Title
4446      */
4447     setTitle: function(str) {
4448         this.titleEl.dom.innerHTML = str;
4449         this.title = str;
4450     },
4451     /**
4452      * Set the body of the Dialog
4453      * @param {String} str new Title
4454      */
4455     setBody: function(str) {
4456         this.bodyEl.dom.innerHTML = str;
4457     },
4458     /**
4459      * Set the body of the Dialog using the template
4460      * @param {Obj} data - apply this data to the template and replace the body contents.
4461      */
4462     applyBody: function(obj)
4463     {
4464         if (!this.tmpl) {
4465             Roo.log("Error - using apply Body without a template");
4466             //code
4467         }
4468         this.tmpl.overwrite(this.bodyEl, obj);
4469     },
4470     
4471     getChildHeight : function(child_nodes)
4472     {
4473         if(
4474             !child_nodes ||
4475             child_nodes.length == 0
4476         ) {
4477             return 0;
4478         }
4479         
4480         var child_height = 0;
4481         
4482         for(var i = 0; i < child_nodes.length; i++) {
4483             
4484             /*
4485             * for modal with tabs...
4486             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4487                 
4488                 var layout_childs = child_nodes[i].childNodes;
4489                 
4490                 for(var j = 0; j < layout_childs.length; j++) {
4491                     
4492                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4493                         
4494                         var layout_body_childs = layout_childs[j].childNodes;
4495                         
4496                         for(var k = 0; k < layout_body_childs.length; k++) {
4497                             
4498                             if(layout_body_childs[k].classList.contains('navbar')) {
4499                                 child_height += layout_body_childs[k].offsetHeight;
4500                                 continue;
4501                             }
4502                             
4503                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4504                                 
4505                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4506                                 
4507                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4508                                     
4509                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4510                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4511                                         continue;
4512                                     }
4513                                     
4514                                 }
4515                                 
4516                             }
4517                             
4518                         }
4519                     }
4520                 }
4521                 continue;
4522             }
4523             */
4524             
4525             child_height += child_nodes[i].offsetHeight;
4526             // Roo.log(child_nodes[i].offsetHeight);
4527         }
4528         
4529         return child_height;
4530     },
4531     toggleHeaderInput : function(is_edit)
4532     {
4533         if (!this.editableTitle) {
4534             return; // not editable.
4535         }
4536         if (is_edit && this.is_header_editing) {
4537             return; // already editing..
4538         }
4539         if (is_edit) {
4540     
4541             this.headerEditEl.dom.value = this.title;
4542             this.headerEditEl.removeClass('d-none');
4543             this.headerEditEl.dom.focus();
4544             this.titleEl.addClass('d-none');
4545             
4546             this.is_header_editing = true;
4547             return
4548         }
4549         // flip back to not editing.
4550         this.title = this.headerEditEl.dom.value;
4551         this.headerEditEl.addClass('d-none');
4552         this.titleEl.removeClass('d-none');
4553         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4554         this.is_header_editing = false;
4555         this.fireEvent('titlechanged', this, this.title);
4556     
4557             
4558         
4559     }
4560
4561 });
4562
4563
4564 Roo.apply(Roo.bootstrap.Modal,  {
4565     /**
4566          * Button config that displays a single OK button
4567          * @type Object
4568          */
4569         OK :  [{
4570             name : 'ok',
4571             weight : 'primary',
4572             html : 'OK'
4573         }],
4574         /**
4575          * Button config that displays Yes and No buttons
4576          * @type Object
4577          */
4578         YESNO : [
4579             {
4580                 name  : 'no',
4581                 html : 'No'
4582             },
4583             {
4584                 name  :'yes',
4585                 weight : 'primary',
4586                 html : 'Yes'
4587             }
4588         ],
4589
4590         /**
4591          * Button config that displays OK and Cancel buttons
4592          * @type Object
4593          */
4594         OKCANCEL : [
4595             {
4596                name : 'cancel',
4597                 html : 'Cancel'
4598             },
4599             {
4600                 name : 'ok',
4601                 weight : 'primary',
4602                 html : 'OK'
4603             }
4604         ],
4605         /**
4606          * Button config that displays Yes, No and Cancel buttons
4607          * @type Object
4608          */
4609         YESNOCANCEL : [
4610             {
4611                 name : 'yes',
4612                 weight : 'primary',
4613                 html : 'Yes'
4614             },
4615             {
4616                 name : 'no',
4617                 html : 'No'
4618             },
4619             {
4620                 name : 'cancel',
4621                 html : 'Cancel'
4622             }
4623         ],
4624         
4625         zIndex : 10001
4626 });
4627
4628 /*
4629  * - LGPL
4630  *
4631  * messagebox - can be used as a replace
4632  * 
4633  */
4634 /**
4635  * @class Roo.MessageBox
4636  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4637  * Example usage:
4638  *<pre><code>
4639 // Basic alert:
4640 Roo.Msg.alert('Status', 'Changes saved successfully.');
4641
4642 // Prompt for user data:
4643 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4644     if (btn == 'ok'){
4645         // process text value...
4646     }
4647 });
4648
4649 // Show a dialog using config options:
4650 Roo.Msg.show({
4651    title:'Save Changes?',
4652    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4653    buttons: Roo.Msg.YESNOCANCEL,
4654    fn: processResult,
4655    animEl: 'elId'
4656 });
4657 </code></pre>
4658  * @singleton
4659  */
4660 Roo.bootstrap.MessageBox = function(){
4661     var dlg, opt, mask, waitTimer;
4662     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4663     var buttons, activeTextEl, bwidth;
4664
4665     
4666     // private
4667     var handleButton = function(button){
4668         dlg.hide();
4669         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4670     };
4671
4672     // private
4673     var handleHide = function(){
4674         if(opt && opt.cls){
4675             dlg.el.removeClass(opt.cls);
4676         }
4677         //if(waitTimer){
4678         //    Roo.TaskMgr.stop(waitTimer);
4679         //    waitTimer = null;
4680         //}
4681     };
4682
4683     // private
4684     var updateButtons = function(b){
4685         var width = 0;
4686         if(!b){
4687             buttons["ok"].hide();
4688             buttons["cancel"].hide();
4689             buttons["yes"].hide();
4690             buttons["no"].hide();
4691             dlg.footerEl.hide();
4692             
4693             return width;
4694         }
4695         dlg.footerEl.show();
4696         for(var k in buttons){
4697             if(typeof buttons[k] != "function"){
4698                 if(b[k]){
4699                     buttons[k].show();
4700                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4701                     width += buttons[k].el.getWidth()+15;
4702                 }else{
4703                     buttons[k].hide();
4704                 }
4705             }
4706         }
4707         return width;
4708     };
4709
4710     // private
4711     var handleEsc = function(d, k, e){
4712         if(opt && opt.closable !== false){
4713             dlg.hide();
4714         }
4715         if(e){
4716             e.stopEvent();
4717         }
4718     };
4719
4720     return {
4721         /**
4722          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4723          * @return {Roo.BasicDialog} The BasicDialog element
4724          */
4725         getDialog : function(){
4726            if(!dlg){
4727                 dlg = new Roo.bootstrap.Modal( {
4728                     //draggable: true,
4729                     //resizable:false,
4730                     //constraintoviewport:false,
4731                     //fixedcenter:true,
4732                     //collapsible : false,
4733                     //shim:true,
4734                     //modal: true,
4735                 //    width: 'auto',
4736                   //  height:100,
4737                     //buttonAlign:"center",
4738                     closeClick : function(){
4739                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4740                             handleButton("no");
4741                         }else{
4742                             handleButton("cancel");
4743                         }
4744                     }
4745                 });
4746                 dlg.render();
4747                 dlg.on("hide", handleHide);
4748                 mask = dlg.mask;
4749                 //dlg.addKeyListener(27, handleEsc);
4750                 buttons = {};
4751                 this.buttons = buttons;
4752                 var bt = this.buttonText;
4753                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4754                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4755                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4756                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4757                 //Roo.log(buttons);
4758                 bodyEl = dlg.bodyEl.createChild({
4759
4760                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4761                         '<textarea class="roo-mb-textarea"></textarea>' +
4762                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4763                 });
4764                 msgEl = bodyEl.dom.firstChild;
4765                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4766                 textboxEl.enableDisplayMode();
4767                 textboxEl.addKeyListener([10,13], function(){
4768                     if(dlg.isVisible() && opt && opt.buttons){
4769                         if(opt.buttons.ok){
4770                             handleButton("ok");
4771                         }else if(opt.buttons.yes){
4772                             handleButton("yes");
4773                         }
4774                     }
4775                 });
4776                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4777                 textareaEl.enableDisplayMode();
4778                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4779                 progressEl.enableDisplayMode();
4780                 
4781                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4782                 var pf = progressEl.dom.firstChild;
4783                 if (pf) {
4784                     pp = Roo.get(pf.firstChild);
4785                     pp.setHeight(pf.offsetHeight);
4786                 }
4787                 
4788             }
4789             return dlg;
4790         },
4791
4792         /**
4793          * Updates the message box body text
4794          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4795          * the XHTML-compliant non-breaking space character '&amp;#160;')
4796          * @return {Roo.MessageBox} This message box
4797          */
4798         updateText : function(text)
4799         {
4800             if(!dlg.isVisible() && !opt.width){
4801                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4802                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4803             }
4804             msgEl.innerHTML = text || '&#160;';
4805       
4806             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4807             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4808             var w = Math.max(
4809                     Math.min(opt.width || cw , this.maxWidth), 
4810                     Math.max(opt.minWidth || this.minWidth, bwidth)
4811             );
4812             if(opt.prompt){
4813                 activeTextEl.setWidth(w);
4814             }
4815             if(dlg.isVisible()){
4816                 dlg.fixedcenter = false;
4817             }
4818             // to big, make it scroll. = But as usual stupid IE does not support
4819             // !important..
4820             
4821             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4822                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4823                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4824             } else {
4825                 bodyEl.dom.style.height = '';
4826                 bodyEl.dom.style.overflowY = '';
4827             }
4828             if (cw > w) {
4829                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4830             } else {
4831                 bodyEl.dom.style.overflowX = '';
4832             }
4833             
4834             dlg.setContentSize(w, bodyEl.getHeight());
4835             if(dlg.isVisible()){
4836                 dlg.fixedcenter = true;
4837             }
4838             return this;
4839         },
4840
4841         /**
4842          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4843          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4844          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4845          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4846          * @return {Roo.MessageBox} This message box
4847          */
4848         updateProgress : function(value, text){
4849             if(text){
4850                 this.updateText(text);
4851             }
4852             
4853             if (pp) { // weird bug on my firefox - for some reason this is not defined
4854                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4855                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4856             }
4857             return this;
4858         },        
4859
4860         /**
4861          * Returns true if the message box is currently displayed
4862          * @return {Boolean} True if the message box is visible, else false
4863          */
4864         isVisible : function(){
4865             return dlg && dlg.isVisible();  
4866         },
4867
4868         /**
4869          * Hides the message box if it is displayed
4870          */
4871         hide : function(){
4872             if(this.isVisible()){
4873                 dlg.hide();
4874             }  
4875         },
4876
4877         /**
4878          * Displays a new message box, or reinitializes an existing message box, based on the config options
4879          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4880          * The following config object properties are supported:
4881          * <pre>
4882 Property    Type             Description
4883 ----------  ---------------  ------------------------------------------------------------------------------------
4884 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4885                                    closes (defaults to undefined)
4886 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4887                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4888 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4889                                    progress and wait dialogs will ignore this property and always hide the
4890                                    close button as they can only be closed programmatically.
4891 cls               String           A custom CSS class to apply to the message box element
4892 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4893                                    displayed (defaults to 75)
4894 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4895                                    function will be btn (the name of the button that was clicked, if applicable,
4896                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4897                                    Progress and wait dialogs will ignore this option since they do not respond to
4898                                    user actions and can only be closed programmatically, so any required function
4899                                    should be called by the same code after it closes the dialog.
4900 icon              String           A CSS class that provides a background image to be used as an icon for
4901                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4902 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4903 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4904 modal             Boolean          False to allow user interaction with the page while the message box is
4905                                    displayed (defaults to true)
4906 msg               String           A string that will replace the existing message box body text (defaults
4907                                    to the XHTML-compliant non-breaking space character '&#160;')
4908 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4909 progress          Boolean          True to display a progress bar (defaults to false)
4910 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4911 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4912 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4913 title             String           The title text
4914 value             String           The string value to set into the active textbox element if displayed
4915 wait              Boolean          True to display a progress bar (defaults to false)
4916 width             Number           The width of the dialog in pixels
4917 </pre>
4918          *
4919          * Example usage:
4920          * <pre><code>
4921 Roo.Msg.show({
4922    title: 'Address',
4923    msg: 'Please enter your address:',
4924    width: 300,
4925    buttons: Roo.MessageBox.OKCANCEL,
4926    multiline: true,
4927    fn: saveAddress,
4928    animEl: 'addAddressBtn'
4929 });
4930 </code></pre>
4931          * @param {Object} config Configuration options
4932          * @return {Roo.MessageBox} This message box
4933          */
4934         show : function(options)
4935         {
4936             
4937             // this causes nightmares if you show one dialog after another
4938             // especially on callbacks..
4939              
4940             if(this.isVisible()){
4941                 
4942                 this.hide();
4943                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4944                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4945                 Roo.log("New Dialog Message:" +  options.msg )
4946                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4947                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4948                 
4949             }
4950             var d = this.getDialog();
4951             opt = options;
4952             d.setTitle(opt.title || "&#160;");
4953             d.closeEl.setDisplayed(opt.closable !== false);
4954             activeTextEl = textboxEl;
4955             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4956             if(opt.prompt){
4957                 if(opt.multiline){
4958                     textboxEl.hide();
4959                     textareaEl.show();
4960                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4961                         opt.multiline : this.defaultTextHeight);
4962                     activeTextEl = textareaEl;
4963                 }else{
4964                     textboxEl.show();
4965                     textareaEl.hide();
4966                 }
4967             }else{
4968                 textboxEl.hide();
4969                 textareaEl.hide();
4970             }
4971             progressEl.setDisplayed(opt.progress === true);
4972             if (opt.progress) {
4973                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4974             }
4975             this.updateProgress(0);
4976             activeTextEl.dom.value = opt.value || "";
4977             if(opt.prompt){
4978                 dlg.setDefaultButton(activeTextEl);
4979             }else{
4980                 var bs = opt.buttons;
4981                 var db = null;
4982                 if(bs && bs.ok){
4983                     db = buttons["ok"];
4984                 }else if(bs && bs.yes){
4985                     db = buttons["yes"];
4986                 }
4987                 dlg.setDefaultButton(db);
4988             }
4989             bwidth = updateButtons(opt.buttons);
4990             this.updateText(opt.msg);
4991             if(opt.cls){
4992                 d.el.addClass(opt.cls);
4993             }
4994             d.proxyDrag = opt.proxyDrag === true;
4995             d.modal = opt.modal !== false;
4996             d.mask = opt.modal !== false ? mask : false;
4997             if(!d.isVisible()){
4998                 // force it to the end of the z-index stack so it gets a cursor in FF
4999                 document.body.appendChild(dlg.el.dom);
5000                 d.animateTarget = null;
5001                 d.show(options.animEl);
5002             }
5003             return this;
5004         },
5005
5006         /**
5007          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5008          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5009          * and closing the message box when the process is complete.
5010          * @param {String} title The title bar text
5011          * @param {String} msg The message box body text
5012          * @return {Roo.MessageBox} This message box
5013          */
5014         progress : function(title, msg){
5015             this.show({
5016                 title : title,
5017                 msg : msg,
5018                 buttons: false,
5019                 progress:true,
5020                 closable:false,
5021                 minWidth: this.minProgressWidth,
5022                 modal : true
5023             });
5024             return this;
5025         },
5026
5027         /**
5028          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5029          * If a callback function is passed it will be called after the user clicks the button, and the
5030          * id of the button that was clicked will be passed as the only parameter to the callback
5031          * (could also be the top-right close button).
5032          * @param {String} title The title bar text
5033          * @param {String} msg The message box body text
5034          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5035          * @param {Object} scope (optional) The scope of the callback function
5036          * @return {Roo.MessageBox} This message box
5037          */
5038         alert : function(title, msg, fn, scope)
5039         {
5040             this.show({
5041                 title : title,
5042                 msg : msg,
5043                 buttons: this.OK,
5044                 fn: fn,
5045                 closable : false,
5046                 scope : scope,
5047                 modal : true
5048             });
5049             return this;
5050         },
5051
5052         /**
5053          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5054          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5055          * You are responsible for closing the message box when the process is complete.
5056          * @param {String} msg The message box body text
5057          * @param {String} title (optional) The title bar text
5058          * @return {Roo.MessageBox} This message box
5059          */
5060         wait : function(msg, title){
5061             this.show({
5062                 title : title,
5063                 msg : msg,
5064                 buttons: false,
5065                 closable:false,
5066                 progress:true,
5067                 modal:true,
5068                 width:300,
5069                 wait:true
5070             });
5071             waitTimer = Roo.TaskMgr.start({
5072                 run: function(i){
5073                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5074                 },
5075                 interval: 1000
5076             });
5077             return this;
5078         },
5079
5080         /**
5081          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5082          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5083          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5084          * @param {String} title The title bar text
5085          * @param {String} msg The message box body text
5086          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5087          * @param {Object} scope (optional) The scope of the callback function
5088          * @return {Roo.MessageBox} This message box
5089          */
5090         confirm : function(title, msg, fn, scope){
5091             this.show({
5092                 title : title,
5093                 msg : msg,
5094                 buttons: this.YESNO,
5095                 fn: fn,
5096                 scope : scope,
5097                 modal : true
5098             });
5099             return this;
5100         },
5101
5102         /**
5103          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5104          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5105          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5106          * (could also be the top-right close button) and the text that was entered will be passed as the two
5107          * parameters to the callback.
5108          * @param {String} title The title bar text
5109          * @param {String} msg The message box body text
5110          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5111          * @param {Object} scope (optional) The scope of the callback function
5112          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5113          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5114          * @return {Roo.MessageBox} This message box
5115          */
5116         prompt : function(title, msg, fn, scope, multiline){
5117             this.show({
5118                 title : title,
5119                 msg : msg,
5120                 buttons: this.OKCANCEL,
5121                 fn: fn,
5122                 minWidth:250,
5123                 scope : scope,
5124                 prompt:true,
5125                 multiline: multiline,
5126                 modal : true
5127             });
5128             return this;
5129         },
5130
5131         /**
5132          * Button config that displays a single OK button
5133          * @type Object
5134          */
5135         OK : {ok:true},
5136         /**
5137          * Button config that displays Yes and No buttons
5138          * @type Object
5139          */
5140         YESNO : {yes:true, no:true},
5141         /**
5142          * Button config that displays OK and Cancel buttons
5143          * @type Object
5144          */
5145         OKCANCEL : {ok:true, cancel:true},
5146         /**
5147          * Button config that displays Yes, No and Cancel buttons
5148          * @type Object
5149          */
5150         YESNOCANCEL : {yes:true, no:true, cancel:true},
5151
5152         /**
5153          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5154          * @type Number
5155          */
5156         defaultTextHeight : 75,
5157         /**
5158          * The maximum width in pixels of the message box (defaults to 600)
5159          * @type Number
5160          */
5161         maxWidth : 600,
5162         /**
5163          * The minimum width in pixels of the message box (defaults to 100)
5164          * @type Number
5165          */
5166         minWidth : 100,
5167         /**
5168          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5169          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5170          * @type Number
5171          */
5172         minProgressWidth : 250,
5173         /**
5174          * An object containing the default button text strings that can be overriden for localized language support.
5175          * Supported properties are: ok, cancel, yes and no.
5176          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5177          * @type Object
5178          */
5179         buttonText : {
5180             ok : "OK",
5181             cancel : "Cancel",
5182             yes : "Yes",
5183             no : "No"
5184         }
5185     };
5186 }();
5187
5188 /**
5189  * Shorthand for {@link Roo.MessageBox}
5190  */
5191 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5192 Roo.Msg = Roo.Msg || Roo.MessageBox;
5193 /*
5194  * - LGPL
5195  *
5196  * navbar
5197  * 
5198  */
5199
5200 /**
5201  * @class Roo.bootstrap.Navbar
5202  * @extends Roo.bootstrap.Component
5203  * Bootstrap Navbar class
5204
5205  * @constructor
5206  * Create a new Navbar
5207  * @param {Object} config The config object
5208  */
5209
5210
5211 Roo.bootstrap.Navbar = function(config){
5212     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5213     this.addEvents({
5214         // raw events
5215         /**
5216          * @event beforetoggle
5217          * Fire before toggle the menu
5218          * @param {Roo.EventObject} e
5219          */
5220         "beforetoggle" : true
5221     });
5222 };
5223
5224 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5225     
5226     
5227    
5228     // private
5229     navItems : false,
5230     loadMask : false,
5231     
5232     
5233     getAutoCreate : function(){
5234         
5235         
5236         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5237         
5238     },
5239     
5240     initEvents :function ()
5241     {
5242         //Roo.log(this.el.select('.navbar-toggle',true));
5243         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5244         
5245         var mark = {
5246             tag: "div",
5247             cls:"x-dlg-mask"
5248         };
5249         
5250         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5251         
5252         var size = this.el.getSize();
5253         this.maskEl.setSize(size.width, size.height);
5254         this.maskEl.enableDisplayMode("block");
5255         this.maskEl.hide();
5256         
5257         if(this.loadMask){
5258             this.maskEl.show();
5259         }
5260     },
5261     
5262     
5263     getChildContainer : function()
5264     {
5265         if (this.el && this.el.select('.collapse').getCount()) {
5266             return this.el.select('.collapse',true).first();
5267         }
5268         
5269         return this.el;
5270     },
5271     
5272     mask : function()
5273     {
5274         this.maskEl.show();
5275     },
5276     
5277     unmask : function()
5278     {
5279         this.maskEl.hide();
5280     },
5281     onToggle : function()
5282     {
5283         
5284         if(this.fireEvent('beforetoggle', this) === false){
5285             return;
5286         }
5287         var ce = this.el.select('.navbar-collapse',true).first();
5288       
5289         if (!ce.hasClass('show')) {
5290            this.expand();
5291         } else {
5292             this.collapse();
5293         }
5294         
5295         
5296     
5297     },
5298     /**
5299      * Expand the navbar pulldown 
5300      */
5301     expand : function ()
5302     {
5303        
5304         var ce = this.el.select('.navbar-collapse',true).first();
5305         if (ce.hasClass('collapsing')) {
5306             return;
5307         }
5308         ce.dom.style.height = '';
5309                // show it...
5310         ce.addClass('in'); // old...
5311         ce.removeClass('collapse');
5312         ce.addClass('show');
5313         var h = ce.getHeight();
5314         Roo.log(h);
5315         ce.removeClass('show');
5316         // at this point we should be able to see it..
5317         ce.addClass('collapsing');
5318         
5319         ce.setHeight(0); // resize it ...
5320         ce.on('transitionend', function() {
5321             //Roo.log('done transition');
5322             ce.removeClass('collapsing');
5323             ce.addClass('show');
5324             ce.removeClass('collapse');
5325
5326             ce.dom.style.height = '';
5327         }, this, { single: true} );
5328         ce.setHeight(h);
5329         ce.dom.scrollTop = 0;
5330     },
5331     /**
5332      * Collapse the navbar pulldown 
5333      */
5334     collapse : function()
5335     {
5336          var ce = this.el.select('.navbar-collapse',true).first();
5337        
5338         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5339             // it's collapsed or collapsing..
5340             return;
5341         }
5342         ce.removeClass('in'); // old...
5343         ce.setHeight(ce.getHeight());
5344         ce.removeClass('show');
5345         ce.addClass('collapsing');
5346         
5347         ce.on('transitionend', function() {
5348             ce.dom.style.height = '';
5349             ce.removeClass('collapsing');
5350             ce.addClass('collapse');
5351         }, this, { single: true} );
5352         ce.setHeight(0);
5353     }
5354     
5355     
5356     
5357 });
5358
5359
5360
5361  
5362
5363  /*
5364  * - LGPL
5365  *
5366  * navbar
5367  * 
5368  */
5369
5370 /**
5371  * @class Roo.bootstrap.NavSimplebar
5372  * @extends Roo.bootstrap.Navbar
5373  * Bootstrap Sidebar class
5374  *
5375  * @cfg {Boolean} inverse is inverted color
5376  * 
5377  * @cfg {String} type (nav | pills | tabs)
5378  * @cfg {Boolean} arrangement stacked | justified
5379  * @cfg {String} align (left | right) alignment
5380  * 
5381  * @cfg {Boolean} main (true|false) main nav bar? default false
5382  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5383  * 
5384  * @cfg {String} tag (header|footer|nav|div) default is nav 
5385
5386  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5387  * 
5388  * 
5389  * @constructor
5390  * Create a new Sidebar
5391  * @param {Object} config The config object
5392  */
5393
5394
5395 Roo.bootstrap.NavSimplebar = function(config){
5396     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5397 };
5398
5399 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5400     
5401     inverse: false,
5402     
5403     type: false,
5404     arrangement: '',
5405     align : false,
5406     
5407     weight : 'light',
5408     
5409     main : false,
5410     
5411     
5412     tag : false,
5413     
5414     
5415     getAutoCreate : function(){
5416         
5417         
5418         var cfg = {
5419             tag : this.tag || 'div',
5420             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5421         };
5422         if (['light','white'].indexOf(this.weight) > -1) {
5423             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5424         }
5425         cfg.cls += ' bg-' + this.weight;
5426         
5427         if (this.inverse) {
5428             cfg.cls += ' navbar-inverse';
5429             
5430         }
5431         
5432         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5433         
5434         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5435             return cfg;
5436         }
5437         
5438         
5439     
5440         
5441         cfg.cn = [
5442             {
5443                 cls: 'nav nav-' + this.xtype,
5444                 tag : 'ul'
5445             }
5446         ];
5447         
5448          
5449         this.type = this.type || 'nav';
5450         if (['tabs','pills'].indexOf(this.type) != -1) {
5451             cfg.cn[0].cls += ' nav-' + this.type
5452         
5453         
5454         } else {
5455             if (this.type!=='nav') {
5456                 Roo.log('nav type must be nav/tabs/pills')
5457             }
5458             cfg.cn[0].cls += ' navbar-nav'
5459         }
5460         
5461         
5462         
5463         
5464         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5465             cfg.cn[0].cls += ' nav-' + this.arrangement;
5466         }
5467         
5468         
5469         if (this.align === 'right') {
5470             cfg.cn[0].cls += ' navbar-right';
5471         }
5472         
5473         
5474         
5475         
5476         return cfg;
5477     
5478         
5479     }
5480     
5481     
5482     
5483 });
5484
5485
5486
5487  
5488
5489  
5490        /*
5491  * - LGPL
5492  *
5493  * navbar
5494  * navbar-fixed-top
5495  * navbar-expand-md  fixed-top 
5496  */
5497
5498 /**
5499  * @class Roo.bootstrap.NavHeaderbar
5500  * @extends Roo.bootstrap.NavSimplebar
5501  * Bootstrap Sidebar class
5502  *
5503  * @cfg {String} brand what is brand
5504  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5505  * @cfg {String} brand_href href of the brand
5506  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5507  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5508  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5509  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5510  * 
5511  * @constructor
5512  * Create a new Sidebar
5513  * @param {Object} config The config object
5514  */
5515
5516
5517 Roo.bootstrap.NavHeaderbar = function(config){
5518     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5519       
5520 };
5521
5522 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5523     
5524     position: '',
5525     brand: '',
5526     brand_href: false,
5527     srButton : true,
5528     autohide : false,
5529     desktopCenter : false,
5530    
5531     
5532     getAutoCreate : function(){
5533         
5534         var   cfg = {
5535             tag: this.nav || 'nav',
5536             cls: 'navbar navbar-expand-md',
5537             role: 'navigation',
5538             cn: []
5539         };
5540         
5541         var cn = cfg.cn;
5542         if (this.desktopCenter) {
5543             cn.push({cls : 'container', cn : []});
5544             cn = cn[0].cn;
5545         }
5546         
5547         if(this.srButton){
5548             var btn = {
5549                 tag: 'button',
5550                 type: 'button',
5551                 cls: 'navbar-toggle navbar-toggler',
5552                 'data-toggle': 'collapse',
5553                 cn: [
5554                     {
5555                         tag: 'span',
5556                         cls: 'sr-only',
5557                         html: 'Toggle navigation'
5558                     },
5559                     {
5560                         tag: 'span',
5561                         cls: 'icon-bar navbar-toggler-icon'
5562                     },
5563                     {
5564                         tag: 'span',
5565                         cls: 'icon-bar'
5566                     },
5567                     {
5568                         tag: 'span',
5569                         cls: 'icon-bar'
5570                     }
5571                 ]
5572             };
5573             
5574             cn.push( Roo.bootstrap.version == 4 ? btn : {
5575                 tag: 'div',
5576                 cls: 'navbar-header',
5577                 cn: [
5578                     btn
5579                 ]
5580             });
5581         }
5582         
5583         cn.push({
5584             tag: 'div',
5585             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5586             cn : []
5587         });
5588         
5589         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5590         
5591         if (['light','white'].indexOf(this.weight) > -1) {
5592             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5593         }
5594         cfg.cls += ' bg-' + this.weight;
5595         
5596         
5597         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5598             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5599             
5600             // tag can override this..
5601             
5602             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5603         }
5604         
5605         if (this.brand !== '') {
5606             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5607             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5608                 tag: 'a',
5609                 href: this.brand_href ? this.brand_href : '#',
5610                 cls: 'navbar-brand',
5611                 cn: [
5612                 this.brand
5613                 ]
5614             });
5615         }
5616         
5617         if(this.main){
5618             cfg.cls += ' main-nav';
5619         }
5620         
5621         
5622         return cfg;
5623
5624         
5625     },
5626     getHeaderChildContainer : function()
5627     {
5628         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5629             return this.el.select('.navbar-header',true).first();
5630         }
5631         
5632         return this.getChildContainer();
5633     },
5634     
5635     getChildContainer : function()
5636     {
5637          
5638         return this.el.select('.roo-navbar-collapse',true).first();
5639          
5640         
5641     },
5642     
5643     initEvents : function()
5644     {
5645         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5646         
5647         if (this.autohide) {
5648             
5649             var prevScroll = 0;
5650             var ft = this.el;
5651             
5652             Roo.get(document).on('scroll',function(e) {
5653                 var ns = Roo.get(document).getScroll().top;
5654                 var os = prevScroll;
5655                 prevScroll = ns;
5656                 
5657                 if(ns > os){
5658                     ft.removeClass('slideDown');
5659                     ft.addClass('slideUp');
5660                     return;
5661                 }
5662                 ft.removeClass('slideUp');
5663                 ft.addClass('slideDown');
5664                  
5665               
5666           },this);
5667         }
5668     }    
5669     
5670 });
5671
5672
5673
5674  
5675
5676  /*
5677  * - LGPL
5678  *
5679  * navbar
5680  * 
5681  */
5682
5683 /**
5684  * @class Roo.bootstrap.NavSidebar
5685  * @extends Roo.bootstrap.Navbar
5686  * Bootstrap Sidebar class
5687  * 
5688  * @constructor
5689  * Create a new Sidebar
5690  * @param {Object} config The config object
5691  */
5692
5693
5694 Roo.bootstrap.NavSidebar = function(config){
5695     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5696 };
5697
5698 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5699     
5700     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5701     
5702     getAutoCreate : function(){
5703         
5704         
5705         return  {
5706             tag: 'div',
5707             cls: 'sidebar sidebar-nav'
5708         };
5709     
5710         
5711     }
5712     
5713     
5714     
5715 });
5716
5717
5718
5719  
5720
5721  /*
5722  * - LGPL
5723  *
5724  * nav group
5725  * 
5726  */
5727
5728 /**
5729  * @class Roo.bootstrap.NavGroup
5730  * @extends Roo.bootstrap.Component
5731  * Bootstrap NavGroup class
5732  * @cfg {String} align (left|right)
5733  * @cfg {Boolean} inverse
5734  * @cfg {String} type (nav|pills|tab) default nav
5735  * @cfg {String} navId - reference Id for navbar.
5736  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5737  * 
5738  * @constructor
5739  * Create a new nav group
5740  * @param {Object} config The config object
5741  */
5742
5743 Roo.bootstrap.NavGroup = function(config){
5744     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5745     this.navItems = [];
5746    
5747     Roo.bootstrap.NavGroup.register(this);
5748      this.addEvents({
5749         /**
5750              * @event changed
5751              * Fires when the active item changes
5752              * @param {Roo.bootstrap.NavGroup} this
5753              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5754              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5755          */
5756         'changed': true
5757      });
5758     
5759 };
5760
5761 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5762     
5763     align: '',
5764     inverse: false,
5765     form: false,
5766     type: 'nav',
5767     navId : '',
5768     // private
5769     pilltype : true,
5770     
5771     navItems : false, 
5772     
5773     getAutoCreate : function()
5774     {
5775         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5776         
5777         cfg = {
5778             tag : 'ul',
5779             cls: 'nav' 
5780         };
5781         if (Roo.bootstrap.version == 4) {
5782             if (['tabs','pills'].indexOf(this.type) != -1) {
5783                 cfg.cls += ' nav-' + this.type; 
5784             } else {
5785                 // trying to remove so header bar can right align top?
5786                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5787                     // do not use on header bar... 
5788                     cfg.cls += ' navbar-nav';
5789                 }
5790             }
5791             
5792         } else {
5793             if (['tabs','pills'].indexOf(this.type) != -1) {
5794                 cfg.cls += ' nav-' + this.type
5795             } else {
5796                 if (this.type !== 'nav') {
5797                     Roo.log('nav type must be nav/tabs/pills')
5798                 }
5799                 cfg.cls += ' navbar-nav'
5800             }
5801         }
5802         
5803         if (this.parent() && this.parent().sidebar) {
5804             cfg = {
5805                 tag: 'ul',
5806                 cls: 'dashboard-menu sidebar-menu'
5807             };
5808             
5809             return cfg;
5810         }
5811         
5812         if (this.form === true) {
5813             cfg = {
5814                 tag: 'form',
5815                 cls: 'navbar-form form-inline'
5816             };
5817             //nav navbar-right ml-md-auto
5818             if (this.align === 'right') {
5819                 cfg.cls += ' navbar-right ml-md-auto';
5820             } else {
5821                 cfg.cls += ' navbar-left';
5822             }
5823         }
5824         
5825         if (this.align === 'right') {
5826             cfg.cls += ' navbar-right ml-md-auto';
5827         } else {
5828             cfg.cls += ' mr-auto';
5829         }
5830         
5831         if (this.inverse) {
5832             cfg.cls += ' navbar-inverse';
5833             
5834         }
5835         
5836         
5837         return cfg;
5838     },
5839     /**
5840     * sets the active Navigation item
5841     * @param {Roo.bootstrap.NavItem} the new current navitem
5842     */
5843     setActiveItem : function(item)
5844     {
5845         var prev = false;
5846         Roo.each(this.navItems, function(v){
5847             if (v == item) {
5848                 return ;
5849             }
5850             if (v.isActive()) {
5851                 v.setActive(false, true);
5852                 prev = v;
5853                 
5854             }
5855             
5856         });
5857
5858         item.setActive(true, true);
5859         this.fireEvent('changed', this, item, prev);
5860         
5861         
5862     },
5863     /**
5864     * gets the active Navigation item
5865     * @return {Roo.bootstrap.NavItem} the current navitem
5866     */
5867     getActive : function()
5868     {
5869         
5870         var prev = false;
5871         Roo.each(this.navItems, function(v){
5872             
5873             if (v.isActive()) {
5874                 prev = v;
5875                 
5876             }
5877             
5878         });
5879         return prev;
5880     },
5881     
5882     indexOfNav : function()
5883     {
5884         
5885         var prev = false;
5886         Roo.each(this.navItems, function(v,i){
5887             
5888             if (v.isActive()) {
5889                 prev = i;
5890                 
5891             }
5892             
5893         });
5894         return prev;
5895     },
5896     /**
5897     * adds a Navigation item
5898     * @param {Roo.bootstrap.NavItem} the navitem to add
5899     */
5900     addItem : function(cfg)
5901     {
5902         if (this.form && Roo.bootstrap.version == 4) {
5903             cfg.tag = 'div';
5904         }
5905         var cn = new Roo.bootstrap.NavItem(cfg);
5906         this.register(cn);
5907         cn.parentId = this.id;
5908         cn.onRender(this.el, null);
5909         return cn;
5910     },
5911     /**
5912     * register a Navigation item
5913     * @param {Roo.bootstrap.NavItem} the navitem to add
5914     */
5915     register : function(item)
5916     {
5917         this.navItems.push( item);
5918         item.navId = this.navId;
5919     
5920     },
5921     
5922     /**
5923     * clear all the Navigation item
5924     */
5925    
5926     clearAll : function()
5927     {
5928         this.navItems = [];
5929         this.el.dom.innerHTML = '';
5930     },
5931     
5932     getNavItem: function(tabId)
5933     {
5934         var ret = false;
5935         Roo.each(this.navItems, function(e) {
5936             if (e.tabId == tabId) {
5937                ret =  e;
5938                return false;
5939             }
5940             return true;
5941             
5942         });
5943         return ret;
5944     },
5945     
5946     setActiveNext : function()
5947     {
5948         var i = this.indexOfNav(this.getActive());
5949         if (i > this.navItems.length) {
5950             return;
5951         }
5952         this.setActiveItem(this.navItems[i+1]);
5953     },
5954     setActivePrev : function()
5955     {
5956         var i = this.indexOfNav(this.getActive());
5957         if (i  < 1) {
5958             return;
5959         }
5960         this.setActiveItem(this.navItems[i-1]);
5961     },
5962     clearWasActive : function(except) {
5963         Roo.each(this.navItems, function(e) {
5964             if (e.tabId != except.tabId && e.was_active) {
5965                e.was_active = false;
5966                return false;
5967             }
5968             return true;
5969             
5970         });
5971     },
5972     getWasActive : function ()
5973     {
5974         var r = false;
5975         Roo.each(this.navItems, function(e) {
5976             if (e.was_active) {
5977                r = e;
5978                return false;
5979             }
5980             return true;
5981             
5982         });
5983         return r;
5984     }
5985     
5986     
5987 });
5988
5989  
5990 Roo.apply(Roo.bootstrap.NavGroup, {
5991     
5992     groups: {},
5993      /**
5994     * register a Navigation Group
5995     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5996     */
5997     register : function(navgrp)
5998     {
5999         this.groups[navgrp.navId] = navgrp;
6000         
6001     },
6002     /**
6003     * fetch a Navigation Group based on the navigation ID
6004     * @param {string} the navgroup to add
6005     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6006     */
6007     get: function(navId) {
6008         if (typeof(this.groups[navId]) == 'undefined') {
6009             return false;
6010             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6011         }
6012         return this.groups[navId] ;
6013     }
6014     
6015     
6016     
6017 });
6018
6019  /*
6020  * - LGPL
6021  *
6022  * row
6023  * 
6024  */
6025
6026 /**
6027  * @class Roo.bootstrap.NavItem
6028  * @extends Roo.bootstrap.Component
6029  * Bootstrap Navbar.NavItem class
6030  * @cfg {String} href  link to
6031  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6032  * @cfg {Boolean} button_outline show and outlined button
6033  * @cfg {String} html content of button
6034  * @cfg {String} badge text inside badge
6035  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6036  * @cfg {String} glyphicon DEPRICATED - use fa
6037  * @cfg {String} icon DEPRICATED - use fa
6038  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6039  * @cfg {Boolean} active Is item active
6040  * @cfg {Boolean} disabled Is item disabled
6041  * @cfg {String} linkcls  Link Class
6042  * @cfg {Boolean} preventDefault (true | false) default false
6043  * @cfg {String} tabId the tab that this item activates.
6044  * @cfg {String} tagtype (a|span) render as a href or span?
6045  * @cfg {Boolean} animateRef (true|false) link to element default false  
6046   
6047  * @constructor
6048  * Create a new Navbar Item
6049  * @param {Object} config The config object
6050  */
6051 Roo.bootstrap.NavItem = function(config){
6052     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6053     this.addEvents({
6054         // raw events
6055         /**
6056          * @event click
6057          * The raw click event for the entire grid.
6058          * @param {Roo.EventObject} e
6059          */
6060         "click" : true,
6061          /**
6062             * @event changed
6063             * Fires when the active item active state changes
6064             * @param {Roo.bootstrap.NavItem} this
6065             * @param {boolean} state the new state
6066              
6067          */
6068         'changed': true,
6069         /**
6070             * @event scrollto
6071             * Fires when scroll to element
6072             * @param {Roo.bootstrap.NavItem} this
6073             * @param {Object} options
6074             * @param {Roo.EventObject} e
6075              
6076          */
6077         'scrollto': true
6078     });
6079    
6080 };
6081
6082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6083     
6084     href: false,
6085     html: '',
6086     badge: '',
6087     icon: false,
6088     fa : false,
6089     glyphicon: false,
6090     active: false,
6091     preventDefault : false,
6092     tabId : false,
6093     tagtype : 'a',
6094     tag: 'li',
6095     disabled : false,
6096     animateRef : false,
6097     was_active : false,
6098     button_weight : '',
6099     button_outline : false,
6100     linkcls : '',
6101     navLink: false,
6102     
6103     getAutoCreate : function(){
6104          
6105         var cfg = {
6106             tag: this.tag,
6107             cls: 'nav-item'
6108         };
6109         
6110         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6111         
6112         if (this.active) {
6113             cfg.cls +=  ' active' ;
6114         }
6115         if (this.disabled) {
6116             cfg.cls += ' disabled';
6117         }
6118         
6119         // BS4 only?
6120         if (this.button_weight.length) {
6121             cfg.tag = this.href ? 'a' : 'button';
6122             cfg.html = this.html || '';
6123             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6124             if (this.href) {
6125                 cfg.href = this.href;
6126             }
6127             if (this.fa) {
6128                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6129             }
6130             
6131             // menu .. should add dropdown-menu class - so no need for carat..
6132             
6133             if (this.badge !== '') {
6134                  
6135                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6136             }
6137             return cfg;
6138         }
6139         
6140         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6141             cfg.cn = [
6142                 {
6143                     tag: this.tagtype,
6144                     href : this.href || "#",
6145                     html: this.html || ''
6146                 }
6147             ];
6148             if (this.tagtype == 'a') {
6149                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6150         
6151             }
6152             if (this.icon) {
6153                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6154             }
6155             if (this.fa) {
6156                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6157             }
6158             if(this.glyphicon) {
6159                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6160             }
6161             
6162             if (this.menu) {
6163                 
6164                 cfg.cn[0].html += " <span class='caret'></span>";
6165              
6166             }
6167             
6168             if (this.badge !== '') {
6169                  
6170                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6171             }
6172         }
6173         
6174         
6175         
6176         return cfg;
6177     },
6178     onRender : function(ct, position)
6179     {
6180        // Roo.log("Call onRender: " + this.xtype);
6181         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6182             this.tag = 'div';
6183         }
6184         
6185         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6186         this.navLink = this.el.select('.nav-link',true).first();
6187         return ret;
6188     },
6189       
6190     
6191     initEvents: function() 
6192     {
6193         if (typeof (this.menu) != 'undefined') {
6194             this.menu.parentType = this.xtype;
6195             this.menu.triggerEl = this.el;
6196             this.menu = this.addxtype(Roo.apply({}, this.menu));
6197         }
6198         
6199         this.el.on('click', this.onClick, this);
6200         
6201         //if(this.tagtype == 'span'){
6202         //    this.el.select('span',true).on('click', this.onClick, this);
6203         //}
6204        
6205         // at this point parent should be available..
6206         this.parent().register(this);
6207     },
6208     
6209     onClick : function(e)
6210     {
6211         if (e.getTarget('.dropdown-menu-item')) {
6212             // did you click on a menu itemm.... - then don't trigger onclick..
6213             return;
6214         }
6215         
6216         if(
6217                 this.preventDefault || 
6218                 this.href == '#' 
6219         ){
6220             Roo.log("NavItem - prevent Default?");
6221             e.preventDefault();
6222         }
6223         
6224         if (this.disabled) {
6225             return;
6226         }
6227         
6228         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6229         if (tg && tg.transition) {
6230             Roo.log("waiting for the transitionend");
6231             return;
6232         }
6233         
6234         
6235         
6236         //Roo.log("fire event clicked");
6237         if(this.fireEvent('click', this, e) === false){
6238             return;
6239         };
6240         
6241         if(this.tagtype == 'span'){
6242             return;
6243         }
6244         
6245         //Roo.log(this.href);
6246         var ael = this.el.select('a',true).first();
6247         //Roo.log(ael);
6248         
6249         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6250             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6251             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6252                 return; // ignore... - it's a 'hash' to another page.
6253             }
6254             Roo.log("NavItem - prevent Default?");
6255             e.preventDefault();
6256             this.scrollToElement(e);
6257         }
6258         
6259         
6260         var p =  this.parent();
6261    
6262         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6263             if (typeof(p.setActiveItem) !== 'undefined') {
6264                 p.setActiveItem(this);
6265             }
6266         }
6267         
6268         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6269         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6270             // remove the collapsed menu expand...
6271             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6272         }
6273     },
6274     
6275     isActive: function () {
6276         return this.active
6277     },
6278     setActive : function(state, fire, is_was_active)
6279     {
6280         if (this.active && !state && this.navId) {
6281             this.was_active = true;
6282             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6283             if (nv) {
6284                 nv.clearWasActive(this);
6285             }
6286             
6287         }
6288         this.active = state;
6289         
6290         if (!state ) {
6291             this.el.removeClass('active');
6292             this.navLink ? this.navLink.removeClass('active') : false;
6293         } else if (!this.el.hasClass('active')) {
6294             
6295             this.el.addClass('active');
6296             if (Roo.bootstrap.version == 4 && this.navLink ) {
6297                 this.navLink.addClass('active');
6298             }
6299             
6300         }
6301         if (fire) {
6302             this.fireEvent('changed', this, state);
6303         }
6304         
6305         // show a panel if it's registered and related..
6306         
6307         if (!this.navId || !this.tabId || !state || is_was_active) {
6308             return;
6309         }
6310         
6311         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6312         if (!tg) {
6313             return;
6314         }
6315         var pan = tg.getPanelByName(this.tabId);
6316         if (!pan) {
6317             return;
6318         }
6319         // if we can not flip to new panel - go back to old nav highlight..
6320         if (false == tg.showPanel(pan)) {
6321             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6322             if (nv) {
6323                 var onav = nv.getWasActive();
6324                 if (onav) {
6325                     onav.setActive(true, false, true);
6326                 }
6327             }
6328             
6329         }
6330         
6331         
6332         
6333     },
6334      // this should not be here...
6335     setDisabled : function(state)
6336     {
6337         this.disabled = state;
6338         if (!state ) {
6339             this.el.removeClass('disabled');
6340         } else if (!this.el.hasClass('disabled')) {
6341             this.el.addClass('disabled');
6342         }
6343         
6344     },
6345     
6346     /**
6347      * Fetch the element to display the tooltip on.
6348      * @return {Roo.Element} defaults to this.el
6349      */
6350     tooltipEl : function()
6351     {
6352         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6353     },
6354     
6355     scrollToElement : function(e)
6356     {
6357         var c = document.body;
6358         
6359         /*
6360          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6361          */
6362         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6363             c = document.documentElement;
6364         }
6365         
6366         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6367         
6368         if(!target){
6369             return;
6370         }
6371
6372         var o = target.calcOffsetsTo(c);
6373         
6374         var options = {
6375             target : target,
6376             value : o[1]
6377         };
6378         
6379         this.fireEvent('scrollto', this, options, e);
6380         
6381         Roo.get(c).scrollTo('top', options.value, true);
6382         
6383         return;
6384     }
6385 });
6386  
6387
6388  /*
6389  * - LGPL
6390  *
6391  * sidebar item
6392  *
6393  *  li
6394  *    <span> icon </span>
6395  *    <span> text </span>
6396  *    <span>badge </span>
6397  */
6398
6399 /**
6400  * @class Roo.bootstrap.NavSidebarItem
6401  * @extends Roo.bootstrap.NavItem
6402  * Bootstrap Navbar.NavSidebarItem class
6403  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6404  * {Boolean} open is the menu open
6405  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6406  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6407  * {String} buttonSize (sm|md|lg)the extra classes for the button
6408  * {Boolean} showArrow show arrow next to the text (default true)
6409  * @constructor
6410  * Create a new Navbar Button
6411  * @param {Object} config The config object
6412  */
6413 Roo.bootstrap.NavSidebarItem = function(config){
6414     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6415     this.addEvents({
6416         // raw events
6417         /**
6418          * @event click
6419          * The raw click event for the entire grid.
6420          * @param {Roo.EventObject} e
6421          */
6422         "click" : true,
6423          /**
6424             * @event changed
6425             * Fires when the active item active state changes
6426             * @param {Roo.bootstrap.NavSidebarItem} this
6427             * @param {boolean} state the new state
6428              
6429          */
6430         'changed': true
6431     });
6432    
6433 };
6434
6435 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6436     
6437     badgeWeight : 'default',
6438     
6439     open: false,
6440     
6441     buttonView : false,
6442     
6443     buttonWeight : 'default',
6444     
6445     buttonSize : 'md',
6446     
6447     showArrow : true,
6448     
6449     getAutoCreate : function(){
6450         
6451         
6452         var a = {
6453                 tag: 'a',
6454                 href : this.href || '#',
6455                 cls: '',
6456                 html : '',
6457                 cn : []
6458         };
6459         
6460         if(this.buttonView){
6461             a = {
6462                 tag: 'button',
6463                 href : this.href || '#',
6464                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6465                 html : this.html,
6466                 cn : []
6467             };
6468         }
6469         
6470         var cfg = {
6471             tag: 'li',
6472             cls: '',
6473             cn: [ a ]
6474         };
6475         
6476         if (this.active) {
6477             cfg.cls += ' active';
6478         }
6479         
6480         if (this.disabled) {
6481             cfg.cls += ' disabled';
6482         }
6483         if (this.open) {
6484             cfg.cls += ' open x-open';
6485         }
6486         // left icon..
6487         if (this.glyphicon || this.icon) {
6488             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6489             a.cn.push({ tag : 'i', cls : c }) ;
6490         }
6491         
6492         if(!this.buttonView){
6493             var span = {
6494                 tag: 'span',
6495                 html : this.html || ''
6496             };
6497
6498             a.cn.push(span);
6499             
6500         }
6501         
6502         if (this.badge !== '') {
6503             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6504         }
6505         
6506         if (this.menu) {
6507             
6508             if(this.showArrow){
6509                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6510             }
6511             
6512             a.cls += ' dropdown-toggle treeview' ;
6513         }
6514         
6515         return cfg;
6516     },
6517     
6518     initEvents : function()
6519     { 
6520         if (typeof (this.menu) != 'undefined') {
6521             this.menu.parentType = this.xtype;
6522             this.menu.triggerEl = this.el;
6523             this.menu = this.addxtype(Roo.apply({}, this.menu));
6524         }
6525         
6526         this.el.on('click', this.onClick, this);
6527         
6528         if(this.badge !== ''){
6529             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6530         }
6531         
6532     },
6533     
6534     onClick : function(e)
6535     {
6536         if(this.disabled){
6537             e.preventDefault();
6538             return;
6539         }
6540         
6541         if(this.preventDefault){
6542             e.preventDefault();
6543         }
6544         
6545         this.fireEvent('click', this, e);
6546     },
6547     
6548     disable : function()
6549     {
6550         this.setDisabled(true);
6551     },
6552     
6553     enable : function()
6554     {
6555         this.setDisabled(false);
6556     },
6557     
6558     setDisabled : function(state)
6559     {
6560         if(this.disabled == state){
6561             return;
6562         }
6563         
6564         this.disabled = state;
6565         
6566         if (state) {
6567             this.el.addClass('disabled');
6568             return;
6569         }
6570         
6571         this.el.removeClass('disabled');
6572         
6573         return;
6574     },
6575     
6576     setActive : function(state)
6577     {
6578         if(this.active == state){
6579             return;
6580         }
6581         
6582         this.active = state;
6583         
6584         if (state) {
6585             this.el.addClass('active');
6586             return;
6587         }
6588         
6589         this.el.removeClass('active');
6590         
6591         return;
6592     },
6593     
6594     isActive: function () 
6595     {
6596         return this.active;
6597     },
6598     
6599     setBadge : function(str)
6600     {
6601         if(!this.badgeEl){
6602             return;
6603         }
6604         
6605         this.badgeEl.dom.innerHTML = str;
6606     }
6607     
6608    
6609      
6610  
6611 });
6612  
6613
6614  /*
6615  * - LGPL
6616  *
6617  *  Breadcrumb Nav
6618  * 
6619  */
6620 Roo.namespace('Roo.bootstrap.breadcrumb');
6621
6622
6623 /**
6624  * @class Roo.bootstrap.breadcrumb.Nav
6625  * @extends Roo.bootstrap.Component
6626  * Bootstrap Breadcrumb Nav Class
6627  *  
6628  * @children Roo.bootstrap.breadcrumb.Item
6629  * 
6630  * @constructor
6631  * Create a new breadcrumb.Nav
6632  * @param {Object} config The config object
6633  */
6634
6635
6636 Roo.bootstrap.breadcrumb.Nav = function(config){
6637     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6638     
6639     
6640 };
6641
6642 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6643     
6644     getAutoCreate : function()
6645     {
6646
6647         var cfg = {
6648             tag: 'nav',
6649             cn : [
6650                 {
6651                     tag : 'ol',
6652                     cls : 'breadcrumb'
6653                 }
6654             ]
6655             
6656         };
6657           
6658         return cfg;
6659     },
6660     
6661     initEvents: function()
6662     {
6663         this.olEl = this.el.select('ol',true).first();    
6664     },
6665     getChildContainer : function()
6666     {
6667         return this.olEl;  
6668     }
6669     
6670 });
6671
6672  /*
6673  * - LGPL
6674  *
6675  *  Breadcrumb Item
6676  * 
6677  */
6678
6679
6680 /**
6681  * @class Roo.bootstrap.breadcrumb.Nav
6682  * @extends Roo.bootstrap.Component
6683  * Bootstrap Breadcrumb Nav Class
6684  *  
6685  * @children Roo.bootstrap.breadcrumb.Component
6686  * @cfg {String} html the content of the link.
6687  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6688  * @cfg {Boolean} active is it active
6689
6690  * 
6691  * @constructor
6692  * Create a new breadcrumb.Nav
6693  * @param {Object} config The config object
6694  */
6695
6696 Roo.bootstrap.breadcrumb.Item = function(config){
6697     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6698     this.addEvents({
6699         // img events
6700         /**
6701          * @event click
6702          * The img click event for the img.
6703          * @param {Roo.EventObject} e
6704          */
6705         "click" : true
6706     });
6707     
6708 };
6709
6710 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6711     
6712     href: false,
6713     html : '',
6714     
6715     getAutoCreate : function()
6716     {
6717
6718         var cfg = {
6719             tag: 'li',
6720             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6721         };
6722         if (this.href !== false) {
6723             cfg.cn = [{
6724                 tag : 'a',
6725                 href : this.href,
6726                 html : this.html
6727             }];
6728         } else {
6729             cfg.html = this.html;
6730         }
6731         
6732         return cfg;
6733     },
6734     
6735     initEvents: function()
6736     {
6737         if (this.href) {
6738             this.el.select('a', true).first().on('click',this.onClick, this)
6739         }
6740         
6741     },
6742     onClick : function(e)
6743     {
6744         e.preventDefault();
6745         this.fireEvent('click',this,  e);
6746     }
6747     
6748 });
6749
6750  /*
6751  * - LGPL
6752  *
6753  * row
6754  * 
6755  */
6756
6757 /**
6758  * @class Roo.bootstrap.Row
6759  * @extends Roo.bootstrap.Component
6760  * Bootstrap Row class (contains columns...)
6761  * 
6762  * @constructor
6763  * Create a new Row
6764  * @param {Object} config The config object
6765  */
6766
6767 Roo.bootstrap.Row = function(config){
6768     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6769 };
6770
6771 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6772     
6773     getAutoCreate : function(){
6774        return {
6775             cls: 'row clearfix'
6776        };
6777     }
6778     
6779     
6780 });
6781
6782  
6783
6784  /*
6785  * - LGPL
6786  *
6787  * pagination
6788  * 
6789  */
6790
6791 /**
6792  * @class Roo.bootstrap.Pagination
6793  * @extends Roo.bootstrap.Component
6794  * Bootstrap Pagination class
6795  * @cfg {String} size xs | sm | md | lg
6796  * @cfg {Boolean} inverse false | true
6797  * 
6798  * @constructor
6799  * Create a new Pagination
6800  * @param {Object} config The config object
6801  */
6802
6803 Roo.bootstrap.Pagination = function(config){
6804     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6805 };
6806
6807 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6808     
6809     cls: false,
6810     size: false,
6811     inverse: false,
6812     
6813     getAutoCreate : function(){
6814         var cfg = {
6815             tag: 'ul',
6816                 cls: 'pagination'
6817         };
6818         if (this.inverse) {
6819             cfg.cls += ' inverse';
6820         }
6821         if (this.html) {
6822             cfg.html=this.html;
6823         }
6824         if (this.cls) {
6825             cfg.cls += " " + this.cls;
6826         }
6827         return cfg;
6828     }
6829    
6830 });
6831
6832  
6833
6834  /*
6835  * - LGPL
6836  *
6837  * Pagination item
6838  * 
6839  */
6840
6841
6842 /**
6843  * @class Roo.bootstrap.PaginationItem
6844  * @extends Roo.bootstrap.Component
6845  * Bootstrap PaginationItem class
6846  * @cfg {String} html text
6847  * @cfg {String} href the link
6848  * @cfg {Boolean} preventDefault (true | false) default true
6849  * @cfg {Boolean} active (true | false) default false
6850  * @cfg {Boolean} disabled default false
6851  * 
6852  * 
6853  * @constructor
6854  * Create a new PaginationItem
6855  * @param {Object} config The config object
6856  */
6857
6858
6859 Roo.bootstrap.PaginationItem = function(config){
6860     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6861     this.addEvents({
6862         // raw events
6863         /**
6864          * @event click
6865          * The raw click event for the entire grid.
6866          * @param {Roo.EventObject} e
6867          */
6868         "click" : true
6869     });
6870 };
6871
6872 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6873     
6874     href : false,
6875     html : false,
6876     preventDefault: true,
6877     active : false,
6878     cls : false,
6879     disabled: false,
6880     
6881     getAutoCreate : function(){
6882         var cfg= {
6883             tag: 'li',
6884             cn: [
6885                 {
6886                     tag : 'a',
6887                     href : this.href ? this.href : '#',
6888                     html : this.html ? this.html : ''
6889                 }
6890             ]
6891         };
6892         
6893         if(this.cls){
6894             cfg.cls = this.cls;
6895         }
6896         
6897         if(this.disabled){
6898             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6899         }
6900         
6901         if(this.active){
6902             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6903         }
6904         
6905         return cfg;
6906     },
6907     
6908     initEvents: function() {
6909         
6910         this.el.on('click', this.onClick, this);
6911         
6912     },
6913     onClick : function(e)
6914     {
6915         Roo.log('PaginationItem on click ');
6916         if(this.preventDefault){
6917             e.preventDefault();
6918         }
6919         
6920         if(this.disabled){
6921             return;
6922         }
6923         
6924         this.fireEvent('click', this, e);
6925     }
6926    
6927 });
6928
6929  
6930
6931  /*
6932  * - LGPL
6933  *
6934  * slider
6935  * 
6936  */
6937
6938
6939 /**
6940  * @class Roo.bootstrap.Slider
6941  * @extends Roo.bootstrap.Component
6942  * Bootstrap Slider class
6943  *    
6944  * @constructor
6945  * Create a new Slider
6946  * @param {Object} config The config object
6947  */
6948
6949 Roo.bootstrap.Slider = function(config){
6950     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6951 };
6952
6953 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6954     
6955     getAutoCreate : function(){
6956         
6957         var cfg = {
6958             tag: 'div',
6959             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6960             cn: [
6961                 {
6962                     tag: 'a',
6963                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6964                 }
6965             ]
6966         };
6967         
6968         return cfg;
6969     }
6970    
6971 });
6972
6973  /*
6974  * Based on:
6975  * Ext JS Library 1.1.1
6976  * Copyright(c) 2006-2007, Ext JS, LLC.
6977  *
6978  * Originally Released Under LGPL - original licence link has changed is not relivant.
6979  *
6980  * Fork - LGPL
6981  * <script type="text/javascript">
6982  */
6983  
6984
6985 /**
6986  * @class Roo.grid.ColumnModel
6987  * @extends Roo.util.Observable
6988  * This is the default implementation of a ColumnModel used by the Grid. It defines
6989  * the columns in the grid.
6990  * <br>Usage:<br>
6991  <pre><code>
6992  var colModel = new Roo.grid.ColumnModel([
6993         {header: "Ticker", width: 60, sortable: true, locked: true},
6994         {header: "Company Name", width: 150, sortable: true},
6995         {header: "Market Cap.", width: 100, sortable: true},
6996         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6997         {header: "Employees", width: 100, sortable: true, resizable: false}
6998  ]);
6999  </code></pre>
7000  * <p>
7001  
7002  * The config options listed for this class are options which may appear in each
7003  * individual column definition.
7004  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7005  * @constructor
7006  * @param {Object} config An Array of column config objects. See this class's
7007  * config objects for details.
7008 */
7009 Roo.grid.ColumnModel = function(config){
7010         /**
7011      * The config passed into the constructor
7012      */
7013     this.config = config;
7014     this.lookup = {};
7015
7016     // if no id, create one
7017     // if the column does not have a dataIndex mapping,
7018     // map it to the order it is in the config
7019     for(var i = 0, len = config.length; i < len; i++){
7020         var c = config[i];
7021         if(typeof c.dataIndex == "undefined"){
7022             c.dataIndex = i;
7023         }
7024         if(typeof c.renderer == "string"){
7025             c.renderer = Roo.util.Format[c.renderer];
7026         }
7027         if(typeof c.id == "undefined"){
7028             c.id = Roo.id();
7029         }
7030         if(c.editor && c.editor.xtype){
7031             c.editor  = Roo.factory(c.editor, Roo.grid);
7032         }
7033         if(c.editor && c.editor.isFormField){
7034             c.editor = new Roo.grid.GridEditor(c.editor);
7035         }
7036         this.lookup[c.id] = c;
7037     }
7038
7039     /**
7040      * The width of columns which have no width specified (defaults to 100)
7041      * @type Number
7042      */
7043     this.defaultWidth = 100;
7044
7045     /**
7046      * Default sortable of columns which have no sortable specified (defaults to false)
7047      * @type Boolean
7048      */
7049     this.defaultSortable = false;
7050
7051     this.addEvents({
7052         /**
7053              * @event widthchange
7054              * Fires when the width of a column changes.
7055              * @param {ColumnModel} this
7056              * @param {Number} columnIndex The column index
7057              * @param {Number} newWidth The new width
7058              */
7059             "widthchange": true,
7060         /**
7061              * @event headerchange
7062              * Fires when the text of a header changes.
7063              * @param {ColumnModel} this
7064              * @param {Number} columnIndex The column index
7065              * @param {Number} newText The new header text
7066              */
7067             "headerchange": true,
7068         /**
7069              * @event hiddenchange
7070              * Fires when a column is hidden or "unhidden".
7071              * @param {ColumnModel} this
7072              * @param {Number} columnIndex The column index
7073              * @param {Boolean} hidden true if hidden, false otherwise
7074              */
7075             "hiddenchange": true,
7076             /**
7077          * @event columnmoved
7078          * Fires when a column is moved.
7079          * @param {ColumnModel} this
7080          * @param {Number} oldIndex
7081          * @param {Number} newIndex
7082          */
7083         "columnmoved" : true,
7084         /**
7085          * @event columlockchange
7086          * Fires when a column's locked state is changed
7087          * @param {ColumnModel} this
7088          * @param {Number} colIndex
7089          * @param {Boolean} locked true if locked
7090          */
7091         "columnlockchange" : true
7092     });
7093     Roo.grid.ColumnModel.superclass.constructor.call(this);
7094 };
7095 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7096     /**
7097      * @cfg {String} header The header text to display in the Grid view.
7098      */
7099     /**
7100      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7101      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7102      * specified, the column's index is used as an index into the Record's data Array.
7103      */
7104     /**
7105      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7106      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7107      */
7108     /**
7109      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7110      * Defaults to the value of the {@link #defaultSortable} property.
7111      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7112      */
7113     /**
7114      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7115      */
7116     /**
7117      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7118      */
7119     /**
7120      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7121      */
7122     /**
7123      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7124      */
7125     /**
7126      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7127      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7128      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7129      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7130      */
7131        /**
7132      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7133      */
7134     /**
7135      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7136      */
7137     /**
7138      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7139      */
7140     /**
7141      * @cfg {String} cursor (Optional)
7142      */
7143     /**
7144      * @cfg {String} tooltip (Optional)
7145      */
7146     /**
7147      * @cfg {Number} xs (Optional)
7148      */
7149     /**
7150      * @cfg {Number} sm (Optional)
7151      */
7152     /**
7153      * @cfg {Number} md (Optional)
7154      */
7155     /**
7156      * @cfg {Number} lg (Optional)
7157      */
7158     /**
7159      * Returns the id of the column at the specified index.
7160      * @param {Number} index The column index
7161      * @return {String} the id
7162      */
7163     getColumnId : function(index){
7164         return this.config[index].id;
7165     },
7166
7167     /**
7168      * Returns the column for a specified id.
7169      * @param {String} id The column id
7170      * @return {Object} the column
7171      */
7172     getColumnById : function(id){
7173         return this.lookup[id];
7174     },
7175
7176     
7177     /**
7178      * Returns the column for a specified dataIndex.
7179      * @param {String} dataIndex The column dataIndex
7180      * @return {Object|Boolean} the column or false if not found
7181      */
7182     getColumnByDataIndex: function(dataIndex){
7183         var index = this.findColumnIndex(dataIndex);
7184         return index > -1 ? this.config[index] : false;
7185     },
7186     
7187     /**
7188      * Returns the index for a specified column id.
7189      * @param {String} id The column id
7190      * @return {Number} the index, or -1 if not found
7191      */
7192     getIndexById : function(id){
7193         for(var i = 0, len = this.config.length; i < len; i++){
7194             if(this.config[i].id == id){
7195                 return i;
7196             }
7197         }
7198         return -1;
7199     },
7200     
7201     /**
7202      * Returns the index for a specified column dataIndex.
7203      * @param {String} dataIndex The column dataIndex
7204      * @return {Number} the index, or -1 if not found
7205      */
7206     
7207     findColumnIndex : function(dataIndex){
7208         for(var i = 0, len = this.config.length; i < len; i++){
7209             if(this.config[i].dataIndex == dataIndex){
7210                 return i;
7211             }
7212         }
7213         return -1;
7214     },
7215     
7216     
7217     moveColumn : function(oldIndex, newIndex){
7218         var c = this.config[oldIndex];
7219         this.config.splice(oldIndex, 1);
7220         this.config.splice(newIndex, 0, c);
7221         this.dataMap = null;
7222         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7223     },
7224
7225     isLocked : function(colIndex){
7226         return this.config[colIndex].locked === true;
7227     },
7228
7229     setLocked : function(colIndex, value, suppressEvent){
7230         if(this.isLocked(colIndex) == value){
7231             return;
7232         }
7233         this.config[colIndex].locked = value;
7234         if(!suppressEvent){
7235             this.fireEvent("columnlockchange", this, colIndex, value);
7236         }
7237     },
7238
7239     getTotalLockedWidth : function(){
7240         var totalWidth = 0;
7241         for(var i = 0; i < this.config.length; i++){
7242             if(this.isLocked(i) && !this.isHidden(i)){
7243                 this.totalWidth += this.getColumnWidth(i);
7244             }
7245         }
7246         return totalWidth;
7247     },
7248
7249     getLockedCount : function(){
7250         for(var i = 0, len = this.config.length; i < len; i++){
7251             if(!this.isLocked(i)){
7252                 return i;
7253             }
7254         }
7255         
7256         return this.config.length;
7257     },
7258
7259     /**
7260      * Returns the number of columns.
7261      * @return {Number}
7262      */
7263     getColumnCount : function(visibleOnly){
7264         if(visibleOnly === true){
7265             var c = 0;
7266             for(var i = 0, len = this.config.length; i < len; i++){
7267                 if(!this.isHidden(i)){
7268                     c++;
7269                 }
7270             }
7271             return c;
7272         }
7273         return this.config.length;
7274     },
7275
7276     /**
7277      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7278      * @param {Function} fn
7279      * @param {Object} scope (optional)
7280      * @return {Array} result
7281      */
7282     getColumnsBy : function(fn, scope){
7283         var r = [];
7284         for(var i = 0, len = this.config.length; i < len; i++){
7285             var c = this.config[i];
7286             if(fn.call(scope||this, c, i) === true){
7287                 r[r.length] = c;
7288             }
7289         }
7290         return r;
7291     },
7292
7293     /**
7294      * Returns true if the specified column is sortable.
7295      * @param {Number} col The column index
7296      * @return {Boolean}
7297      */
7298     isSortable : function(col){
7299         if(typeof this.config[col].sortable == "undefined"){
7300             return this.defaultSortable;
7301         }
7302         return this.config[col].sortable;
7303     },
7304
7305     /**
7306      * Returns the rendering (formatting) function defined for the column.
7307      * @param {Number} col The column index.
7308      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7309      */
7310     getRenderer : function(col){
7311         if(!this.config[col].renderer){
7312             return Roo.grid.ColumnModel.defaultRenderer;
7313         }
7314         return this.config[col].renderer;
7315     },
7316
7317     /**
7318      * Sets the rendering (formatting) function for a column.
7319      * @param {Number} col The column index
7320      * @param {Function} fn The function to use to process the cell's raw data
7321      * to return HTML markup for the grid view. The render function is called with
7322      * the following parameters:<ul>
7323      * <li>Data value.</li>
7324      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7325      * <li>css A CSS style string to apply to the table cell.</li>
7326      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7327      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7328      * <li>Row index</li>
7329      * <li>Column index</li>
7330      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7331      */
7332     setRenderer : function(col, fn){
7333         this.config[col].renderer = fn;
7334     },
7335
7336     /**
7337      * Returns the width for the specified column.
7338      * @param {Number} col The column index
7339      * @return {Number}
7340      */
7341     getColumnWidth : function(col){
7342         return this.config[col].width * 1 || this.defaultWidth;
7343     },
7344
7345     /**
7346      * Sets the width for a column.
7347      * @param {Number} col The column index
7348      * @param {Number} width The new width
7349      */
7350     setColumnWidth : function(col, width, suppressEvent){
7351         this.config[col].width = width;
7352         this.totalWidth = null;
7353         if(!suppressEvent){
7354              this.fireEvent("widthchange", this, col, width);
7355         }
7356     },
7357
7358     /**
7359      * Returns the total width of all columns.
7360      * @param {Boolean} includeHidden True to include hidden column widths
7361      * @return {Number}
7362      */
7363     getTotalWidth : function(includeHidden){
7364         if(!this.totalWidth){
7365             this.totalWidth = 0;
7366             for(var i = 0, len = this.config.length; i < len; i++){
7367                 if(includeHidden || !this.isHidden(i)){
7368                     this.totalWidth += this.getColumnWidth(i);
7369                 }
7370             }
7371         }
7372         return this.totalWidth;
7373     },
7374
7375     /**
7376      * Returns the header for the specified column.
7377      * @param {Number} col The column index
7378      * @return {String}
7379      */
7380     getColumnHeader : function(col){
7381         return this.config[col].header;
7382     },
7383
7384     /**
7385      * Sets the header for a column.
7386      * @param {Number} col The column index
7387      * @param {String} header The new header
7388      */
7389     setColumnHeader : function(col, header){
7390         this.config[col].header = header;
7391         this.fireEvent("headerchange", this, col, header);
7392     },
7393
7394     /**
7395      * Returns the tooltip for the specified column.
7396      * @param {Number} col The column index
7397      * @return {String}
7398      */
7399     getColumnTooltip : function(col){
7400             return this.config[col].tooltip;
7401     },
7402     /**
7403      * Sets the tooltip for a column.
7404      * @param {Number} col The column index
7405      * @param {String} tooltip The new tooltip
7406      */
7407     setColumnTooltip : function(col, tooltip){
7408             this.config[col].tooltip = tooltip;
7409     },
7410
7411     /**
7412      * Returns the dataIndex for the specified column.
7413      * @param {Number} col The column index
7414      * @return {Number}
7415      */
7416     getDataIndex : function(col){
7417         return this.config[col].dataIndex;
7418     },
7419
7420     /**
7421      * Sets the dataIndex for a column.
7422      * @param {Number} col The column index
7423      * @param {Number} dataIndex The new dataIndex
7424      */
7425     setDataIndex : function(col, dataIndex){
7426         this.config[col].dataIndex = dataIndex;
7427     },
7428
7429     
7430     
7431     /**
7432      * Returns true if the cell is editable.
7433      * @param {Number} colIndex The column index
7434      * @param {Number} rowIndex The row index - this is nto actually used..?
7435      * @return {Boolean}
7436      */
7437     isCellEditable : function(colIndex, rowIndex){
7438         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7439     },
7440
7441     /**
7442      * Returns the editor defined for the cell/column.
7443      * return false or null to disable editing.
7444      * @param {Number} colIndex The column index
7445      * @param {Number} rowIndex The row index
7446      * @return {Object}
7447      */
7448     getCellEditor : function(colIndex, rowIndex){
7449         return this.config[colIndex].editor;
7450     },
7451
7452     /**
7453      * Sets if a column is editable.
7454      * @param {Number} col The column index
7455      * @param {Boolean} editable True if the column is editable
7456      */
7457     setEditable : function(col, editable){
7458         this.config[col].editable = editable;
7459     },
7460
7461
7462     /**
7463      * Returns true if the column is hidden.
7464      * @param {Number} colIndex The column index
7465      * @return {Boolean}
7466      */
7467     isHidden : function(colIndex){
7468         return this.config[colIndex].hidden;
7469     },
7470
7471
7472     /**
7473      * Returns true if the column width cannot be changed
7474      */
7475     isFixed : function(colIndex){
7476         return this.config[colIndex].fixed;
7477     },
7478
7479     /**
7480      * Returns true if the column can be resized
7481      * @return {Boolean}
7482      */
7483     isResizable : function(colIndex){
7484         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7485     },
7486     /**
7487      * Sets if a column is hidden.
7488      * @param {Number} colIndex The column index
7489      * @param {Boolean} hidden True if the column is hidden
7490      */
7491     setHidden : function(colIndex, hidden){
7492         this.config[colIndex].hidden = hidden;
7493         this.totalWidth = null;
7494         this.fireEvent("hiddenchange", this, colIndex, hidden);
7495     },
7496
7497     /**
7498      * Sets the editor for a column.
7499      * @param {Number} col The column index
7500      * @param {Object} editor The editor object
7501      */
7502     setEditor : function(col, editor){
7503         this.config[col].editor = editor;
7504     }
7505 });
7506
7507 Roo.grid.ColumnModel.defaultRenderer = function(value)
7508 {
7509     if(typeof value == "object") {
7510         return value;
7511     }
7512         if(typeof value == "string" && value.length < 1){
7513             return "&#160;";
7514         }
7515     
7516         return String.format("{0}", value);
7517 };
7518
7519 // Alias for backwards compatibility
7520 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7521 /*
7522  * Based on:
7523  * Ext JS Library 1.1.1
7524  * Copyright(c) 2006-2007, Ext JS, LLC.
7525  *
7526  * Originally Released Under LGPL - original licence link has changed is not relivant.
7527  *
7528  * Fork - LGPL
7529  * <script type="text/javascript">
7530  */
7531  
7532 /**
7533  * @class Roo.LoadMask
7534  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7535  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7536  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7537  * element's UpdateManager load indicator and will be destroyed after the initial load.
7538  * @constructor
7539  * Create a new LoadMask
7540  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7541  * @param {Object} config The config object
7542  */
7543 Roo.LoadMask = function(el, config){
7544     this.el = Roo.get(el);
7545     Roo.apply(this, config);
7546     if(this.store){
7547         this.store.on('beforeload', this.onBeforeLoad, this);
7548         this.store.on('load', this.onLoad, this);
7549         this.store.on('loadexception', this.onLoadException, this);
7550         this.removeMask = false;
7551     }else{
7552         var um = this.el.getUpdateManager();
7553         um.showLoadIndicator = false; // disable the default indicator
7554         um.on('beforeupdate', this.onBeforeLoad, this);
7555         um.on('update', this.onLoad, this);
7556         um.on('failure', this.onLoad, this);
7557         this.removeMask = true;
7558     }
7559 };
7560
7561 Roo.LoadMask.prototype = {
7562     /**
7563      * @cfg {Boolean} removeMask
7564      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7565      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7566      */
7567     /**
7568      * @cfg {String} msg
7569      * The text to display in a centered loading message box (defaults to 'Loading...')
7570      */
7571     msg : 'Loading...',
7572     /**
7573      * @cfg {String} msgCls
7574      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7575      */
7576     msgCls : 'x-mask-loading',
7577
7578     /**
7579      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7580      * @type Boolean
7581      */
7582     disabled: false,
7583
7584     /**
7585      * Disables the mask to prevent it from being displayed
7586      */
7587     disable : function(){
7588        this.disabled = true;
7589     },
7590
7591     /**
7592      * Enables the mask so that it can be displayed
7593      */
7594     enable : function(){
7595         this.disabled = false;
7596     },
7597     
7598     onLoadException : function()
7599     {
7600         Roo.log(arguments);
7601         
7602         if (typeof(arguments[3]) != 'undefined') {
7603             Roo.MessageBox.alert("Error loading",arguments[3]);
7604         } 
7605         /*
7606         try {
7607             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7608                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7609             }   
7610         } catch(e) {
7611             
7612         }
7613         */
7614     
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617     // private
7618     onLoad : function()
7619     {
7620         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7621     },
7622
7623     // private
7624     onBeforeLoad : function(){
7625         if(!this.disabled){
7626             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7627         }
7628     },
7629
7630     // private
7631     destroy : function(){
7632         if(this.store){
7633             this.store.un('beforeload', this.onBeforeLoad, this);
7634             this.store.un('load', this.onLoad, this);
7635             this.store.un('loadexception', this.onLoadException, this);
7636         }else{
7637             var um = this.el.getUpdateManager();
7638             um.un('beforeupdate', this.onBeforeLoad, this);
7639             um.un('update', this.onLoad, this);
7640             um.un('failure', this.onLoad, this);
7641         }
7642     }
7643 };/*
7644  * - LGPL
7645  *
7646  * table
7647  * 
7648  */
7649
7650 /**
7651  * @class Roo.bootstrap.Table
7652  * @extends Roo.bootstrap.Component
7653  * Bootstrap Table class
7654  * @cfg {String} cls table class
7655  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7656  * @cfg {String} bgcolor Specifies the background color for a table
7657  * @cfg {Number} border Specifies whether the table cells should have borders or not
7658  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7659  * @cfg {Number} cellspacing Specifies the space between cells
7660  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7661  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7662  * @cfg {String} sortable Specifies that the table should be sortable
7663  * @cfg {String} summary Specifies a summary of the content of a table
7664  * @cfg {Number} width Specifies the width of a table
7665  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7666  * 
7667  * @cfg {boolean} striped Should the rows be alternative striped
7668  * @cfg {boolean} bordered Add borders to the table
7669  * @cfg {boolean} hover Add hover highlighting
7670  * @cfg {boolean} condensed Format condensed
7671  * @cfg {boolean} responsive Format condensed
7672  * @cfg {Boolean} loadMask (true|false) default false
7673  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7674  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7675  * @cfg {Boolean} rowSelection (true|false) default false
7676  * @cfg {Boolean} cellSelection (true|false) default false
7677  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7678  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7679  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7680  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7681  
7682  * 
7683  * @constructor
7684  * Create a new Table
7685  * @param {Object} config The config object
7686  */
7687
7688 Roo.bootstrap.Table = function(config){
7689     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7690     
7691   
7692     
7693     // BC...
7694     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7695     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7696     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7697     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7698     
7699     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7700     if (this.sm) {
7701         this.sm.grid = this;
7702         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7703         this.sm = this.selModel;
7704         this.sm.xmodule = this.xmodule || false;
7705     }
7706     
7707     if (this.cm && typeof(this.cm.config) == 'undefined') {
7708         this.colModel = new Roo.grid.ColumnModel(this.cm);
7709         this.cm = this.colModel;
7710         this.cm.xmodule = this.xmodule || false;
7711     }
7712     if (this.store) {
7713         this.store= Roo.factory(this.store, Roo.data);
7714         this.ds = this.store;
7715         this.ds.xmodule = this.xmodule || false;
7716          
7717     }
7718     if (this.footer && this.store) {
7719         this.footer.dataSource = this.ds;
7720         this.footer = Roo.factory(this.footer);
7721     }
7722     
7723     /** @private */
7724     this.addEvents({
7725         /**
7726          * @event cellclick
7727          * Fires when a cell is clicked
7728          * @param {Roo.bootstrap.Table} this
7729          * @param {Roo.Element} el
7730          * @param {Number} rowIndex
7731          * @param {Number} columnIndex
7732          * @param {Roo.EventObject} e
7733          */
7734         "cellclick" : true,
7735         /**
7736          * @event celldblclick
7737          * Fires when a cell is double clicked
7738          * @param {Roo.bootstrap.Table} this
7739          * @param {Roo.Element} el
7740          * @param {Number} rowIndex
7741          * @param {Number} columnIndex
7742          * @param {Roo.EventObject} e
7743          */
7744         "celldblclick" : true,
7745         /**
7746          * @event rowclick
7747          * Fires when a row is clicked
7748          * @param {Roo.bootstrap.Table} this
7749          * @param {Roo.Element} el
7750          * @param {Number} rowIndex
7751          * @param {Roo.EventObject} e
7752          */
7753         "rowclick" : true,
7754         /**
7755          * @event rowdblclick
7756          * Fires when a row is double clicked
7757          * @param {Roo.bootstrap.Table} this
7758          * @param {Roo.Element} el
7759          * @param {Number} rowIndex
7760          * @param {Roo.EventObject} e
7761          */
7762         "rowdblclick" : true,
7763         /**
7764          * @event mouseover
7765          * Fires when a mouseover occur
7766          * @param {Roo.bootstrap.Table} this
7767          * @param {Roo.Element} el
7768          * @param {Number} rowIndex
7769          * @param {Number} columnIndex
7770          * @param {Roo.EventObject} e
7771          */
7772         "mouseover" : true,
7773         /**
7774          * @event mouseout
7775          * Fires when a mouseout occur
7776          * @param {Roo.bootstrap.Table} this
7777          * @param {Roo.Element} el
7778          * @param {Number} rowIndex
7779          * @param {Number} columnIndex
7780          * @param {Roo.EventObject} e
7781          */
7782         "mouseout" : true,
7783         /**
7784          * @event rowclass
7785          * Fires when a row is rendered, so you can change add a style to it.
7786          * @param {Roo.bootstrap.Table} this
7787          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7788          */
7789         'rowclass' : true,
7790           /**
7791          * @event rowsrendered
7792          * Fires when all the  rows have been rendered
7793          * @param {Roo.bootstrap.Table} this
7794          */
7795         'rowsrendered' : true,
7796         /**
7797          * @event contextmenu
7798          * The raw contextmenu event for the entire grid.
7799          * @param {Roo.EventObject} e
7800          */
7801         "contextmenu" : true,
7802         /**
7803          * @event rowcontextmenu
7804          * Fires when a row is right clicked
7805          * @param {Roo.bootstrap.Table} this
7806          * @param {Number} rowIndex
7807          * @param {Roo.EventObject} e
7808          */
7809         "rowcontextmenu" : true,
7810         /**
7811          * @event cellcontextmenu
7812          * Fires when a cell is right clicked
7813          * @param {Roo.bootstrap.Table} this
7814          * @param {Number} rowIndex
7815          * @param {Number} cellIndex
7816          * @param {Roo.EventObject} e
7817          */
7818          "cellcontextmenu" : true,
7819          /**
7820          * @event headercontextmenu
7821          * Fires when a header is right clicked
7822          * @param {Roo.bootstrap.Table} this
7823          * @param {Number} columnIndex
7824          * @param {Roo.EventObject} e
7825          */
7826         "headercontextmenu" : true
7827     });
7828 };
7829
7830 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7831     
7832     cls: false,
7833     align: false,
7834     bgcolor: false,
7835     border: false,
7836     cellpadding: false,
7837     cellspacing: false,
7838     frame: false,
7839     rules: false,
7840     sortable: false,
7841     summary: false,
7842     width: false,
7843     striped : false,
7844     scrollBody : false,
7845     bordered: false,
7846     hover:  false,
7847     condensed : false,
7848     responsive : false,
7849     sm : false,
7850     cm : false,
7851     store : false,
7852     loadMask : false,
7853     footerShow : true,
7854     headerShow : true,
7855   
7856     rowSelection : false,
7857     cellSelection : false,
7858     layout : false,
7859     
7860     // Roo.Element - the tbody
7861     mainBody: false,
7862     // Roo.Element - thead element
7863     mainHead: false,
7864     
7865     container: false, // used by gridpanel...
7866     
7867     lazyLoad : false,
7868     
7869     CSS : Roo.util.CSS,
7870     
7871     auto_hide_footer : false,
7872     
7873     getAutoCreate : function()
7874     {
7875         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7876         
7877         cfg = {
7878             tag: 'table',
7879             cls : 'table',
7880             cn : []
7881         };
7882         if (this.scrollBody) {
7883             cfg.cls += ' table-body-fixed';
7884         }    
7885         if (this.striped) {
7886             cfg.cls += ' table-striped';
7887         }
7888         
7889         if (this.hover) {
7890             cfg.cls += ' table-hover';
7891         }
7892         if (this.bordered) {
7893             cfg.cls += ' table-bordered';
7894         }
7895         if (this.condensed) {
7896             cfg.cls += ' table-condensed';
7897         }
7898         if (this.responsive) {
7899             cfg.cls += ' table-responsive';
7900         }
7901         
7902         if (this.cls) {
7903             cfg.cls+=  ' ' +this.cls;
7904         }
7905         
7906         // this lot should be simplifed...
7907         var _t = this;
7908         var cp = [
7909             'align',
7910             'bgcolor',
7911             'border',
7912             'cellpadding',
7913             'cellspacing',
7914             'frame',
7915             'rules',
7916             'sortable',
7917             'summary',
7918             'width'
7919         ].forEach(function(k) {
7920             if (_t[k]) {
7921                 cfg[k] = _t[k];
7922             }
7923         });
7924         
7925         
7926         if (this.layout) {
7927             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7928         }
7929         
7930         if(this.store || this.cm){
7931             if(this.headerShow){
7932                 cfg.cn.push(this.renderHeader());
7933             }
7934             
7935             cfg.cn.push(this.renderBody());
7936             
7937             if(this.footerShow){
7938                 cfg.cn.push(this.renderFooter());
7939             }
7940             // where does this come from?
7941             //cfg.cls+=  ' TableGrid';
7942         }
7943         
7944         return { cn : [ cfg ] };
7945     },
7946     
7947     initEvents : function()
7948     {   
7949         if(!this.store || !this.cm){
7950             return;
7951         }
7952         if (this.selModel) {
7953             this.selModel.initEvents();
7954         }
7955         
7956         
7957         //Roo.log('initEvents with ds!!!!');
7958         
7959         this.mainBody = this.el.select('tbody', true).first();
7960         this.mainHead = this.el.select('thead', true).first();
7961         this.mainFoot = this.el.select('tfoot', true).first();
7962         
7963         
7964         
7965         var _this = this;
7966         
7967         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7968             e.on('click', _this.sort, _this);
7969         });
7970         
7971         this.mainBody.on("click", this.onClick, this);
7972         this.mainBody.on("dblclick", this.onDblClick, this);
7973         
7974         // why is this done????? = it breaks dialogs??
7975         //this.parent().el.setStyle('position', 'relative');
7976         
7977         
7978         if (this.footer) {
7979             this.footer.parentId = this.id;
7980             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7981             
7982             if(this.lazyLoad){
7983                 this.el.select('tfoot tr td').first().addClass('hide');
7984             }
7985         } 
7986         
7987         if(this.loadMask) {
7988             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7989         }
7990         
7991         this.store.on('load', this.onLoad, this);
7992         this.store.on('beforeload', this.onBeforeLoad, this);
7993         this.store.on('update', this.onUpdate, this);
7994         this.store.on('add', this.onAdd, this);
7995         this.store.on("clear", this.clear, this);
7996         
7997         this.el.on("contextmenu", this.onContextMenu, this);
7998         
7999         this.mainBody.on('scroll', this.onBodyScroll, this);
8000         
8001         this.cm.on("headerchange", this.onHeaderChange, this);
8002         
8003         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8004         
8005     },
8006     
8007     onContextMenu : function(e, t)
8008     {
8009         this.processEvent("contextmenu", e);
8010     },
8011     
8012     processEvent : function(name, e)
8013     {
8014         if (name != 'touchstart' ) {
8015             this.fireEvent(name, e);    
8016         }
8017         
8018         var t = e.getTarget();
8019         
8020         var cell = Roo.get(t);
8021         
8022         if(!cell){
8023             return;
8024         }
8025         
8026         if(cell.findParent('tfoot', false, true)){
8027             return;
8028         }
8029         
8030         if(cell.findParent('thead', false, true)){
8031             
8032             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8033                 cell = Roo.get(t).findParent('th', false, true);
8034                 if (!cell) {
8035                     Roo.log("failed to find th in thead?");
8036                     Roo.log(e.getTarget());
8037                     return;
8038                 }
8039             }
8040             
8041             var cellIndex = cell.dom.cellIndex;
8042             
8043             var ename = name == 'touchstart' ? 'click' : name;
8044             this.fireEvent("header" + ename, this, cellIndex, e);
8045             
8046             return;
8047         }
8048         
8049         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8050             cell = Roo.get(t).findParent('td', false, true);
8051             if (!cell) {
8052                 Roo.log("failed to find th in tbody?");
8053                 Roo.log(e.getTarget());
8054                 return;
8055             }
8056         }
8057         
8058         var row = cell.findParent('tr', false, true);
8059         var cellIndex = cell.dom.cellIndex;
8060         var rowIndex = row.dom.rowIndex - 1;
8061         
8062         if(row !== false){
8063             
8064             this.fireEvent("row" + name, this, rowIndex, e);
8065             
8066             if(cell !== false){
8067             
8068                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8069             }
8070         }
8071         
8072     },
8073     
8074     onMouseover : function(e, el)
8075     {
8076         var cell = Roo.get(el);
8077         
8078         if(!cell){
8079             return;
8080         }
8081         
8082         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8083             cell = cell.findParent('td', false, true);
8084         }
8085         
8086         var row = cell.findParent('tr', false, true);
8087         var cellIndex = cell.dom.cellIndex;
8088         var rowIndex = row.dom.rowIndex - 1; // start from 0
8089         
8090         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8091         
8092     },
8093     
8094     onMouseout : function(e, el)
8095     {
8096         var cell = Roo.get(el);
8097         
8098         if(!cell){
8099             return;
8100         }
8101         
8102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8103             cell = cell.findParent('td', false, true);
8104         }
8105         
8106         var row = cell.findParent('tr', false, true);
8107         var cellIndex = cell.dom.cellIndex;
8108         var rowIndex = row.dom.rowIndex - 1; // start from 0
8109         
8110         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8111         
8112     },
8113     
8114     onClick : function(e, el)
8115     {
8116         var cell = Roo.get(el);
8117         
8118         if(!cell || (!this.cellSelection && !this.rowSelection)){
8119             return;
8120         }
8121         
8122         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8123             cell = cell.findParent('td', false, true);
8124         }
8125         
8126         if(!cell || typeof(cell) == 'undefined'){
8127             return;
8128         }
8129         
8130         var row = cell.findParent('tr', false, true);
8131         
8132         if(!row || typeof(row) == 'undefined'){
8133             return;
8134         }
8135         
8136         var cellIndex = cell.dom.cellIndex;
8137         var rowIndex = this.getRowIndex(row);
8138         
8139         // why??? - should these not be based on SelectionModel?
8140         if(this.cellSelection){
8141             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8142         }
8143         
8144         if(this.rowSelection){
8145             this.fireEvent('rowclick', this, row, rowIndex, e);
8146         }
8147         
8148         
8149     },
8150         
8151     onDblClick : function(e,el)
8152     {
8153         var cell = Roo.get(el);
8154         
8155         if(!cell || (!this.cellSelection && !this.rowSelection)){
8156             return;
8157         }
8158         
8159         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8160             cell = cell.findParent('td', false, true);
8161         }
8162         
8163         if(!cell || typeof(cell) == 'undefined'){
8164             return;
8165         }
8166         
8167         var row = cell.findParent('tr', false, true);
8168         
8169         if(!row || typeof(row) == 'undefined'){
8170             return;
8171         }
8172         
8173         var cellIndex = cell.dom.cellIndex;
8174         var rowIndex = this.getRowIndex(row);
8175         
8176         if(this.cellSelection){
8177             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8178         }
8179         
8180         if(this.rowSelection){
8181             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8182         }
8183     },
8184     
8185     sort : function(e,el)
8186     {
8187         var col = Roo.get(el);
8188         
8189         if(!col.hasClass('sortable')){
8190             return;
8191         }
8192         
8193         var sort = col.attr('sort');
8194         var dir = 'ASC';
8195         
8196         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8197             dir = 'DESC';
8198         }
8199         
8200         this.store.sortInfo = {field : sort, direction : dir};
8201         
8202         if (this.footer) {
8203             Roo.log("calling footer first");
8204             this.footer.onClick('first');
8205         } else {
8206         
8207             this.store.load({ params : { start : 0 } });
8208         }
8209     },
8210     
8211     renderHeader : function()
8212     {
8213         var header = {
8214             tag: 'thead',
8215             cn : []
8216         };
8217         
8218         var cm = this.cm;
8219         this.totalWidth = 0;
8220         
8221         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8222             
8223             var config = cm.config[i];
8224             
8225             var c = {
8226                 tag: 'th',
8227                 cls : 'x-hcol-' + i,
8228                 style : '',
8229                 html: cm.getColumnHeader(i)
8230             };
8231             
8232             var hh = '';
8233             
8234             if(typeof(config.sortable) != 'undefined' && config.sortable){
8235                 c.cls = 'sortable';
8236                 c.html = '<i class="glyphicon"></i>' + c.html;
8237             }
8238             
8239             // could use BS4 hidden-..-down 
8240             
8241             if(typeof(config.lgHeader) != 'undefined'){
8242                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8243             }
8244             
8245             if(typeof(config.mdHeader) != 'undefined'){
8246                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8247             }
8248             
8249             if(typeof(config.smHeader) != 'undefined'){
8250                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8251             }
8252             
8253             if(typeof(config.xsHeader) != 'undefined'){
8254                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8255             }
8256             
8257             if(hh.length){
8258                 c.html = hh;
8259             }
8260             
8261             if(typeof(config.tooltip) != 'undefined'){
8262                 c.tooltip = config.tooltip;
8263             }
8264             
8265             if(typeof(config.colspan) != 'undefined'){
8266                 c.colspan = config.colspan;
8267             }
8268             
8269             if(typeof(config.hidden) != 'undefined' && config.hidden){
8270                 c.style += ' display:none;';
8271             }
8272             
8273             if(typeof(config.dataIndex) != 'undefined'){
8274                 c.sort = config.dataIndex;
8275             }
8276             
8277            
8278             
8279             if(typeof(config.align) != 'undefined' && config.align.length){
8280                 c.style += ' text-align:' + config.align + ';';
8281             }
8282             
8283             if(typeof(config.width) != 'undefined'){
8284                 c.style += ' width:' + config.width + 'px;';
8285                 this.totalWidth += config.width;
8286             } else {
8287                 this.totalWidth += 100; // assume minimum of 100 per column?
8288             }
8289             
8290             if(typeof(config.cls) != 'undefined'){
8291                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8292             }
8293             
8294             ['xs','sm','md','lg'].map(function(size){
8295                 
8296                 if(typeof(config[size]) == 'undefined'){
8297                     return;
8298                 }
8299                  
8300                 if (!config[size]) { // 0 = hidden
8301                     // BS 4 '0' is treated as hide that column and below.
8302                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8303                     return;
8304                 }
8305                 
8306                 c.cls += ' col-' + size + '-' + config[size] + (
8307                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8308                 );
8309                 
8310                 
8311             });
8312             
8313             header.cn.push(c)
8314         }
8315         
8316         return header;
8317     },
8318     
8319     renderBody : function()
8320     {
8321         var body = {
8322             tag: 'tbody',
8323             cn : [
8324                 {
8325                     tag: 'tr',
8326                     cn : [
8327                         {
8328                             tag : 'td',
8329                             colspan :  this.cm.getColumnCount()
8330                         }
8331                     ]
8332                 }
8333             ]
8334         };
8335         
8336         return body;
8337     },
8338     
8339     renderFooter : function()
8340     {
8341         var footer = {
8342             tag: 'tfoot',
8343             cn : [
8344                 {
8345                     tag: 'tr',
8346                     cn : [
8347                         {
8348                             tag : 'td',
8349                             colspan :  this.cm.getColumnCount()
8350                         }
8351                     ]
8352                 }
8353             ]
8354         };
8355         
8356         return footer;
8357     },
8358     
8359     
8360     
8361     onLoad : function()
8362     {
8363 //        Roo.log('ds onload');
8364         this.clear();
8365         
8366         var _this = this;
8367         var cm = this.cm;
8368         var ds = this.store;
8369         
8370         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8371             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8372             if (_this.store.sortInfo) {
8373                     
8374                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8375                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8376                 }
8377                 
8378                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8379                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8380                 }
8381             }
8382         });
8383         
8384         var tbody =  this.mainBody;
8385               
8386         if(ds.getCount() > 0){
8387             ds.data.each(function(d,rowIndex){
8388                 var row =  this.renderRow(cm, ds, rowIndex);
8389                 
8390                 tbody.createChild(row);
8391                 
8392                 var _this = this;
8393                 
8394                 if(row.cellObjects.length){
8395                     Roo.each(row.cellObjects, function(r){
8396                         _this.renderCellObject(r);
8397                     })
8398                 }
8399                 
8400             }, this);
8401         }
8402         
8403         var tfoot = this.el.select('tfoot', true).first();
8404         
8405         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8406             
8407             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8408             
8409             var total = this.ds.getTotalCount();
8410             
8411             if(this.footer.pageSize < total){
8412                 this.mainFoot.show();
8413             }
8414         }
8415         
8416         Roo.each(this.el.select('tbody td', true).elements, function(e){
8417             e.on('mouseover', _this.onMouseover, _this);
8418         });
8419         
8420         Roo.each(this.el.select('tbody td', true).elements, function(e){
8421             e.on('mouseout', _this.onMouseout, _this);
8422         });
8423         this.fireEvent('rowsrendered', this);
8424         
8425         this.autoSize();
8426     },
8427     
8428     
8429     onUpdate : function(ds,record)
8430     {
8431         this.refreshRow(record);
8432         this.autoSize();
8433     },
8434     
8435     onRemove : function(ds, record, index, isUpdate){
8436         if(isUpdate !== true){
8437             this.fireEvent("beforerowremoved", this, index, record);
8438         }
8439         var bt = this.mainBody.dom;
8440         
8441         var rows = this.el.select('tbody > tr', true).elements;
8442         
8443         if(typeof(rows[index]) != 'undefined'){
8444             bt.removeChild(rows[index].dom);
8445         }
8446         
8447 //        if(bt.rows[index]){
8448 //            bt.removeChild(bt.rows[index]);
8449 //        }
8450         
8451         if(isUpdate !== true){
8452             //this.stripeRows(index);
8453             //this.syncRowHeights(index, index);
8454             //this.layout();
8455             this.fireEvent("rowremoved", this, index, record);
8456         }
8457     },
8458     
8459     onAdd : function(ds, records, rowIndex)
8460     {
8461         //Roo.log('on Add called');
8462         // - note this does not handle multiple adding very well..
8463         var bt = this.mainBody.dom;
8464         for (var i =0 ; i < records.length;i++) {
8465             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8466             //Roo.log(records[i]);
8467             //Roo.log(this.store.getAt(rowIndex+i));
8468             this.insertRow(this.store, rowIndex + i, false);
8469             return;
8470         }
8471         
8472     },
8473     
8474     
8475     refreshRow : function(record){
8476         var ds = this.store, index;
8477         if(typeof record == 'number'){
8478             index = record;
8479             record = ds.getAt(index);
8480         }else{
8481             index = ds.indexOf(record);
8482             if (index < 0) {
8483                 return; // should not happen - but seems to 
8484             }
8485         }
8486         this.insertRow(ds, index, true);
8487         this.autoSize();
8488         this.onRemove(ds, record, index+1, true);
8489         this.autoSize();
8490         //this.syncRowHeights(index, index);
8491         //this.layout();
8492         this.fireEvent("rowupdated", this, index, record);
8493     },
8494     
8495     insertRow : function(dm, rowIndex, isUpdate){
8496         
8497         if(!isUpdate){
8498             this.fireEvent("beforerowsinserted", this, rowIndex);
8499         }
8500             //var s = this.getScrollState();
8501         var row = this.renderRow(this.cm, this.store, rowIndex);
8502         // insert before rowIndex..
8503         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8504         
8505         var _this = this;
8506                 
8507         if(row.cellObjects.length){
8508             Roo.each(row.cellObjects, function(r){
8509                 _this.renderCellObject(r);
8510             })
8511         }
8512             
8513         if(!isUpdate){
8514             this.fireEvent("rowsinserted", this, rowIndex);
8515             //this.syncRowHeights(firstRow, lastRow);
8516             //this.stripeRows(firstRow);
8517             //this.layout();
8518         }
8519         
8520     },
8521     
8522     
8523     getRowDom : function(rowIndex)
8524     {
8525         var rows = this.el.select('tbody > tr', true).elements;
8526         
8527         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8528         
8529     },
8530     // returns the object tree for a tr..
8531   
8532     
8533     renderRow : function(cm, ds, rowIndex) 
8534     {
8535         var d = ds.getAt(rowIndex);
8536         
8537         var row = {
8538             tag : 'tr',
8539             cls : 'x-row-' + rowIndex,
8540             cn : []
8541         };
8542             
8543         var cellObjects = [];
8544         
8545         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8546             var config = cm.config[i];
8547             
8548             var renderer = cm.getRenderer(i);
8549             var value = '';
8550             var id = false;
8551             
8552             if(typeof(renderer) !== 'undefined'){
8553                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8554             }
8555             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8556             // and are rendered into the cells after the row is rendered - using the id for the element.
8557             
8558             if(typeof(value) === 'object'){
8559                 id = Roo.id();
8560                 cellObjects.push({
8561                     container : id,
8562                     cfg : value 
8563                 })
8564             }
8565             
8566             var rowcfg = {
8567                 record: d,
8568                 rowIndex : rowIndex,
8569                 colIndex : i,
8570                 rowClass : ''
8571             };
8572
8573             this.fireEvent('rowclass', this, rowcfg);
8574             
8575             var td = {
8576                 tag: 'td',
8577                 cls : rowcfg.rowClass + ' x-col-' + i,
8578                 style: '',
8579                 html: (typeof(value) === 'object') ? '' : value
8580             };
8581             
8582             if (id) {
8583                 td.id = id;
8584             }
8585             
8586             if(typeof(config.colspan) != 'undefined'){
8587                 td.colspan = config.colspan;
8588             }
8589             
8590             if(typeof(config.hidden) != 'undefined' && config.hidden){
8591                 td.style += ' display:none;';
8592             }
8593             
8594             if(typeof(config.align) != 'undefined' && config.align.length){
8595                 td.style += ' text-align:' + config.align + ';';
8596             }
8597             if(typeof(config.valign) != 'undefined' && config.valign.length){
8598                 td.style += ' vertical-align:' + config.valign + ';';
8599             }
8600             
8601             if(typeof(config.width) != 'undefined'){
8602                 td.style += ' width:' +  config.width + 'px;';
8603             }
8604             
8605             if(typeof(config.cursor) != 'undefined'){
8606                 td.style += ' cursor:' +  config.cursor + ';';
8607             }
8608             
8609             if(typeof(config.cls) != 'undefined'){
8610                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8611             }
8612             
8613             ['xs','sm','md','lg'].map(function(size){
8614                 
8615                 if(typeof(config[size]) == 'undefined'){
8616                     return;
8617                 }
8618                 
8619                 
8620                   
8621                 if (!config[size]) { // 0 = hidden
8622                     // BS 4 '0' is treated as hide that column and below.
8623                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8624                     return;
8625                 }
8626                 
8627                 td.cls += ' col-' + size + '-' + config[size] + (
8628                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8629                 );
8630                  
8631
8632             });
8633             
8634             row.cn.push(td);
8635            
8636         }
8637         
8638         row.cellObjects = cellObjects;
8639         
8640         return row;
8641           
8642     },
8643     
8644     
8645     
8646     onBeforeLoad : function()
8647     {
8648         
8649     },
8650      /**
8651      * Remove all rows
8652      */
8653     clear : function()
8654     {
8655         this.el.select('tbody', true).first().dom.innerHTML = '';
8656     },
8657     /**
8658      * Show or hide a row.
8659      * @param {Number} rowIndex to show or hide
8660      * @param {Boolean} state hide
8661      */
8662     setRowVisibility : function(rowIndex, state)
8663     {
8664         var bt = this.mainBody.dom;
8665         
8666         var rows = this.el.select('tbody > tr', true).elements;
8667         
8668         if(typeof(rows[rowIndex]) == 'undefined'){
8669             return;
8670         }
8671         rows[rowIndex].dom.style.display = state ? '' : 'none';
8672     },
8673     
8674     
8675     getSelectionModel : function(){
8676         if(!this.selModel){
8677             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8678         }
8679         return this.selModel;
8680     },
8681     /*
8682      * Render the Roo.bootstrap object from renderder
8683      */
8684     renderCellObject : function(r)
8685     {
8686         var _this = this;
8687         
8688         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8689         
8690         var t = r.cfg.render(r.container);
8691         
8692         if(r.cfg.cn){
8693             Roo.each(r.cfg.cn, function(c){
8694                 var child = {
8695                     container: t.getChildContainer(),
8696                     cfg: c
8697                 };
8698                 _this.renderCellObject(child);
8699             })
8700         }
8701     },
8702     
8703     getRowIndex : function(row)
8704     {
8705         var rowIndex = -1;
8706         
8707         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8708             if(el != row){
8709                 return;
8710             }
8711             
8712             rowIndex = index;
8713         });
8714         
8715         return rowIndex;
8716     },
8717      /**
8718      * Returns the grid's underlying element = used by panel.Grid
8719      * @return {Element} The element
8720      */
8721     getGridEl : function(){
8722         return this.el;
8723     },
8724      /**
8725      * Forces a resize - used by panel.Grid
8726      * @return {Element} The element
8727      */
8728     autoSize : function()
8729     {
8730         //var ctr = Roo.get(this.container.dom.parentElement);
8731         var ctr = Roo.get(this.el.dom);
8732         
8733         var thd = this.getGridEl().select('thead',true).first();
8734         var tbd = this.getGridEl().select('tbody', true).first();
8735         var tfd = this.getGridEl().select('tfoot', true).first();
8736         
8737         var cw = ctr.getWidth();
8738         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8739         
8740         if (tbd) {
8741             
8742             tbd.setWidth(ctr.getWidth());
8743             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8744             // this needs fixing for various usage - currently only hydra job advers I think..
8745             //tdb.setHeight(
8746             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8747             //); 
8748             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8749             cw -= barsize;
8750         }
8751         cw = Math.max(cw, this.totalWidth);
8752         this.getGridEl().select('tbody tr',true).setWidth(cw);
8753         
8754         // resize 'expandable coloumn?
8755         
8756         return; // we doe not have a view in this design..
8757         
8758     },
8759     onBodyScroll: function()
8760     {
8761         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8762         if(this.mainHead){
8763             this.mainHead.setStyle({
8764                 'position' : 'relative',
8765                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8766             });
8767         }
8768         
8769         if(this.lazyLoad){
8770             
8771             var scrollHeight = this.mainBody.dom.scrollHeight;
8772             
8773             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8774             
8775             var height = this.mainBody.getHeight();
8776             
8777             if(scrollHeight - height == scrollTop) {
8778                 
8779                 var total = this.ds.getTotalCount();
8780                 
8781                 if(this.footer.cursor + this.footer.pageSize < total){
8782                     
8783                     this.footer.ds.load({
8784                         params : {
8785                             start : this.footer.cursor + this.footer.pageSize,
8786                             limit : this.footer.pageSize
8787                         },
8788                         add : true
8789                     });
8790                 }
8791             }
8792             
8793         }
8794     },
8795     
8796     onHeaderChange : function()
8797     {
8798         var header = this.renderHeader();
8799         var table = this.el.select('table', true).first();
8800         
8801         this.mainHead.remove();
8802         this.mainHead = table.createChild(header, this.mainBody, false);
8803     },
8804     
8805     onHiddenChange : function(colModel, colIndex, hidden)
8806     {
8807         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8808         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8809         
8810         this.CSS.updateRule(thSelector, "display", "");
8811         this.CSS.updateRule(tdSelector, "display", "");
8812         
8813         if(hidden){
8814             this.CSS.updateRule(thSelector, "display", "none");
8815             this.CSS.updateRule(tdSelector, "display", "none");
8816         }
8817         
8818         this.onHeaderChange();
8819         this.onLoad();
8820     },
8821     
8822     setColumnWidth: function(col_index, width)
8823     {
8824         // width = "md-2 xs-2..."
8825         if(!this.colModel.config[col_index]) {
8826             return;
8827         }
8828         
8829         var w = width.split(" ");
8830         
8831         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8832         
8833         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8834         
8835         
8836         for(var j = 0; j < w.length; j++) {
8837             
8838             if(!w[j]) {
8839                 continue;
8840             }
8841             
8842             var size_cls = w[j].split("-");
8843             
8844             if(!Number.isInteger(size_cls[1] * 1)) {
8845                 continue;
8846             }
8847             
8848             if(!this.colModel.config[col_index][size_cls[0]]) {
8849                 continue;
8850             }
8851             
8852             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8853                 continue;
8854             }
8855             
8856             h_row[0].classList.replace(
8857                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8858                 "col-"+size_cls[0]+"-"+size_cls[1]
8859             );
8860             
8861             for(var i = 0; i < rows.length; i++) {
8862                 
8863                 var size_cls = w[j].split("-");
8864                 
8865                 if(!Number.isInteger(size_cls[1] * 1)) {
8866                     continue;
8867                 }
8868                 
8869                 if(!this.colModel.config[col_index][size_cls[0]]) {
8870                     continue;
8871                 }
8872                 
8873                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8874                     continue;
8875                 }
8876                 
8877                 rows[i].classList.replace(
8878                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8879                     "col-"+size_cls[0]+"-"+size_cls[1]
8880                 );
8881             }
8882             
8883             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8884         }
8885     }
8886 });
8887
8888  
8889
8890  /*
8891  * - LGPL
8892  *
8893  * table cell
8894  * 
8895  */
8896
8897 /**
8898  * @class Roo.bootstrap.TableCell
8899  * @extends Roo.bootstrap.Component
8900  * Bootstrap TableCell class
8901  * @cfg {String} html cell contain text
8902  * @cfg {String} cls cell class
8903  * @cfg {String} tag cell tag (td|th) default td
8904  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8905  * @cfg {String} align Aligns the content in a cell
8906  * @cfg {String} axis Categorizes cells
8907  * @cfg {String} bgcolor Specifies the background color of a cell
8908  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8909  * @cfg {Number} colspan Specifies the number of columns a cell should span
8910  * @cfg {String} headers Specifies one or more header cells a cell is related to
8911  * @cfg {Number} height Sets the height of a cell
8912  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8913  * @cfg {Number} rowspan Sets the number of rows a cell should span
8914  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8915  * @cfg {String} valign Vertical aligns the content in a cell
8916  * @cfg {Number} width Specifies the width of a cell
8917  * 
8918  * @constructor
8919  * Create a new TableCell
8920  * @param {Object} config The config object
8921  */
8922
8923 Roo.bootstrap.TableCell = function(config){
8924     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8925 };
8926
8927 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8928     
8929     html: false,
8930     cls: false,
8931     tag: false,
8932     abbr: false,
8933     align: false,
8934     axis: false,
8935     bgcolor: false,
8936     charoff: false,
8937     colspan: false,
8938     headers: false,
8939     height: false,
8940     nowrap: false,
8941     rowspan: false,
8942     scope: false,
8943     valign: false,
8944     width: false,
8945     
8946     
8947     getAutoCreate : function(){
8948         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8949         
8950         cfg = {
8951             tag: 'td'
8952         };
8953         
8954         if(this.tag){
8955             cfg.tag = this.tag;
8956         }
8957         
8958         if (this.html) {
8959             cfg.html=this.html
8960         }
8961         if (this.cls) {
8962             cfg.cls=this.cls
8963         }
8964         if (this.abbr) {
8965             cfg.abbr=this.abbr
8966         }
8967         if (this.align) {
8968             cfg.align=this.align
8969         }
8970         if (this.axis) {
8971             cfg.axis=this.axis
8972         }
8973         if (this.bgcolor) {
8974             cfg.bgcolor=this.bgcolor
8975         }
8976         if (this.charoff) {
8977             cfg.charoff=this.charoff
8978         }
8979         if (this.colspan) {
8980             cfg.colspan=this.colspan
8981         }
8982         if (this.headers) {
8983             cfg.headers=this.headers
8984         }
8985         if (this.height) {
8986             cfg.height=this.height
8987         }
8988         if (this.nowrap) {
8989             cfg.nowrap=this.nowrap
8990         }
8991         if (this.rowspan) {
8992             cfg.rowspan=this.rowspan
8993         }
8994         if (this.scope) {
8995             cfg.scope=this.scope
8996         }
8997         if (this.valign) {
8998             cfg.valign=this.valign
8999         }
9000         if (this.width) {
9001             cfg.width=this.width
9002         }
9003         
9004         
9005         return cfg;
9006     }
9007    
9008 });
9009
9010  
9011
9012  /*
9013  * - LGPL
9014  *
9015  * table row
9016  * 
9017  */
9018
9019 /**
9020  * @class Roo.bootstrap.TableRow
9021  * @extends Roo.bootstrap.Component
9022  * Bootstrap TableRow class
9023  * @cfg {String} cls row class
9024  * @cfg {String} align Aligns the content in a table row
9025  * @cfg {String} bgcolor Specifies a background color for a table row
9026  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9027  * @cfg {String} valign Vertical aligns the content in a table row
9028  * 
9029  * @constructor
9030  * Create a new TableRow
9031  * @param {Object} config The config object
9032  */
9033
9034 Roo.bootstrap.TableRow = function(config){
9035     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9036 };
9037
9038 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9039     
9040     cls: false,
9041     align: false,
9042     bgcolor: false,
9043     charoff: false,
9044     valign: false,
9045     
9046     getAutoCreate : function(){
9047         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9048         
9049         cfg = {
9050             tag: 'tr'
9051         };
9052             
9053         if(this.cls){
9054             cfg.cls = this.cls;
9055         }
9056         if(this.align){
9057             cfg.align = this.align;
9058         }
9059         if(this.bgcolor){
9060             cfg.bgcolor = this.bgcolor;
9061         }
9062         if(this.charoff){
9063             cfg.charoff = this.charoff;
9064         }
9065         if(this.valign){
9066             cfg.valign = this.valign;
9067         }
9068         
9069         return cfg;
9070     }
9071    
9072 });
9073
9074  
9075
9076  /*
9077  * - LGPL
9078  *
9079  * table body
9080  * 
9081  */
9082
9083 /**
9084  * @class Roo.bootstrap.TableBody
9085  * @extends Roo.bootstrap.Component
9086  * Bootstrap TableBody class
9087  * @cfg {String} cls element class
9088  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9089  * @cfg {String} align Aligns the content inside the element
9090  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9091  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9092  * 
9093  * @constructor
9094  * Create a new TableBody
9095  * @param {Object} config The config object
9096  */
9097
9098 Roo.bootstrap.TableBody = function(config){
9099     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9100 };
9101
9102 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9103     
9104     cls: false,
9105     tag: false,
9106     align: false,
9107     charoff: false,
9108     valign: false,
9109     
9110     getAutoCreate : function(){
9111         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9112         
9113         cfg = {
9114             tag: 'tbody'
9115         };
9116             
9117         if (this.cls) {
9118             cfg.cls=this.cls
9119         }
9120         if(this.tag){
9121             cfg.tag = this.tag;
9122         }
9123         
9124         if(this.align){
9125             cfg.align = this.align;
9126         }
9127         if(this.charoff){
9128             cfg.charoff = this.charoff;
9129         }
9130         if(this.valign){
9131             cfg.valign = this.valign;
9132         }
9133         
9134         return cfg;
9135     }
9136     
9137     
9138 //    initEvents : function()
9139 //    {
9140 //        
9141 //        if(!this.store){
9142 //            return;
9143 //        }
9144 //        
9145 //        this.store = Roo.factory(this.store, Roo.data);
9146 //        this.store.on('load', this.onLoad, this);
9147 //        
9148 //        this.store.load();
9149 //        
9150 //    },
9151 //    
9152 //    onLoad: function () 
9153 //    {   
9154 //        this.fireEvent('load', this);
9155 //    }
9156 //    
9157 //   
9158 });
9159
9160  
9161
9162  /*
9163  * Based on:
9164  * Ext JS Library 1.1.1
9165  * Copyright(c) 2006-2007, Ext JS, LLC.
9166  *
9167  * Originally Released Under LGPL - original licence link has changed is not relivant.
9168  *
9169  * Fork - LGPL
9170  * <script type="text/javascript">
9171  */
9172
9173 // as we use this in bootstrap.
9174 Roo.namespace('Roo.form');
9175  /**
9176  * @class Roo.form.Action
9177  * Internal Class used to handle form actions
9178  * @constructor
9179  * @param {Roo.form.BasicForm} el The form element or its id
9180  * @param {Object} config Configuration options
9181  */
9182
9183  
9184  
9185 // define the action interface
9186 Roo.form.Action = function(form, options){
9187     this.form = form;
9188     this.options = options || {};
9189 };
9190 /**
9191  * Client Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.CLIENT_INVALID = 'client';
9195 /**
9196  * Server Validation Failed
9197  * @const 
9198  */
9199 Roo.form.Action.SERVER_INVALID = 'server';
9200  /**
9201  * Connect to Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.CONNECT_FAILURE = 'connect';
9205 /**
9206  * Reading Data from Server Failed
9207  * @const 
9208  */
9209 Roo.form.Action.LOAD_FAILURE = 'load';
9210
9211 Roo.form.Action.prototype = {
9212     type : 'default',
9213     failureType : undefined,
9214     response : undefined,
9215     result : undefined,
9216
9217     // interface method
9218     run : function(options){
9219
9220     },
9221
9222     // interface method
9223     success : function(response){
9224
9225     },
9226
9227     // interface method
9228     handleResponse : function(response){
9229
9230     },
9231
9232     // default connection failure
9233     failure : function(response){
9234         
9235         this.response = response;
9236         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9237         this.form.afterAction(this, false);
9238     },
9239
9240     processResponse : function(response){
9241         this.response = response;
9242         if(!response.responseText){
9243             return true;
9244         }
9245         this.result = this.handleResponse(response);
9246         return this.result;
9247     },
9248
9249     // utility functions used internally
9250     getUrl : function(appendParams){
9251         var url = this.options.url || this.form.url || this.form.el.dom.action;
9252         if(appendParams){
9253             var p = this.getParams();
9254             if(p){
9255                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9256             }
9257         }
9258         return url;
9259     },
9260
9261     getMethod : function(){
9262         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9263     },
9264
9265     getParams : function(){
9266         var bp = this.form.baseParams;
9267         var p = this.options.params;
9268         if(p){
9269             if(typeof p == "object"){
9270                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9271             }else if(typeof p == 'string' && bp){
9272                 p += '&' + Roo.urlEncode(bp);
9273             }
9274         }else if(bp){
9275             p = Roo.urlEncode(bp);
9276         }
9277         return p;
9278     },
9279
9280     createCallback : function(){
9281         return {
9282             success: this.success,
9283             failure: this.failure,
9284             scope: this,
9285             timeout: (this.form.timeout*1000),
9286             upload: this.form.fileUpload ? this.success : undefined
9287         };
9288     }
9289 };
9290
9291 Roo.form.Action.Submit = function(form, options){
9292     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9293 };
9294
9295 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9296     type : 'submit',
9297
9298     haveProgress : false,
9299     uploadComplete : false,
9300     
9301     // uploadProgress indicator.
9302     uploadProgress : function()
9303     {
9304         if (!this.form.progressUrl) {
9305             return;
9306         }
9307         
9308         if (!this.haveProgress) {
9309             Roo.MessageBox.progress("Uploading", "Uploading");
9310         }
9311         if (this.uploadComplete) {
9312            Roo.MessageBox.hide();
9313            return;
9314         }
9315         
9316         this.haveProgress = true;
9317    
9318         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9319         
9320         var c = new Roo.data.Connection();
9321         c.request({
9322             url : this.form.progressUrl,
9323             params: {
9324                 id : uid
9325             },
9326             method: 'GET',
9327             success : function(req){
9328                //console.log(data);
9329                 var rdata = false;
9330                 var edata;
9331                 try  {
9332                    rdata = Roo.decode(req.responseText)
9333                 } catch (e) {
9334                     Roo.log("Invalid data from server..");
9335                     Roo.log(edata);
9336                     return;
9337                 }
9338                 if (!rdata || !rdata.success) {
9339                     Roo.log(rdata);
9340                     Roo.MessageBox.alert(Roo.encode(rdata));
9341                     return;
9342                 }
9343                 var data = rdata.data;
9344                 
9345                 if (this.uploadComplete) {
9346                    Roo.MessageBox.hide();
9347                    return;
9348                 }
9349                    
9350                 if (data){
9351                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9352                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9353                     );
9354                 }
9355                 this.uploadProgress.defer(2000,this);
9356             },
9357        
9358             failure: function(data) {
9359                 Roo.log('progress url failed ');
9360                 Roo.log(data);
9361             },
9362             scope : this
9363         });
9364            
9365     },
9366     
9367     
9368     run : function()
9369     {
9370         // run get Values on the form, so it syncs any secondary forms.
9371         this.form.getValues();
9372         
9373         var o = this.options;
9374         var method = this.getMethod();
9375         var isPost = method == 'POST';
9376         if(o.clientValidation === false || this.form.isValid()){
9377             
9378             if (this.form.progressUrl) {
9379                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9380                     (new Date() * 1) + '' + Math.random());
9381                     
9382             } 
9383             
9384             
9385             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9386                 form:this.form.el.dom,
9387                 url:this.getUrl(!isPost),
9388                 method: method,
9389                 params:isPost ? this.getParams() : null,
9390                 isUpload: this.form.fileUpload,
9391                 formData : this.form.formData
9392             }));
9393             
9394             this.uploadProgress();
9395
9396         }else if (o.clientValidation !== false){ // client validation failed
9397             this.failureType = Roo.form.Action.CLIENT_INVALID;
9398             this.form.afterAction(this, false);
9399         }
9400     },
9401
9402     success : function(response)
9403     {
9404         this.uploadComplete= true;
9405         if (this.haveProgress) {
9406             Roo.MessageBox.hide();
9407         }
9408         
9409         
9410         var result = this.processResponse(response);
9411         if(result === true || result.success){
9412             this.form.afterAction(this, true);
9413             return;
9414         }
9415         if(result.errors){
9416             this.form.markInvalid(result.errors);
9417             this.failureType = Roo.form.Action.SERVER_INVALID;
9418         }
9419         this.form.afterAction(this, false);
9420     },
9421     failure : function(response)
9422     {
9423         this.uploadComplete= true;
9424         if (this.haveProgress) {
9425             Roo.MessageBox.hide();
9426         }
9427         
9428         this.response = response;
9429         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9430         this.form.afterAction(this, false);
9431     },
9432     
9433     handleResponse : function(response){
9434         if(this.form.errorReader){
9435             var rs = this.form.errorReader.read(response);
9436             var errors = [];
9437             if(rs.records){
9438                 for(var i = 0, len = rs.records.length; i < len; i++) {
9439                     var r = rs.records[i];
9440                     errors[i] = r.data;
9441                 }
9442             }
9443             if(errors.length < 1){
9444                 errors = null;
9445             }
9446             return {
9447                 success : rs.success,
9448                 errors : errors
9449             };
9450         }
9451         var ret = false;
9452         try {
9453             ret = Roo.decode(response.responseText);
9454         } catch (e) {
9455             ret = {
9456                 success: false,
9457                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9458                 errors : []
9459             };
9460         }
9461         return ret;
9462         
9463     }
9464 });
9465
9466
9467 Roo.form.Action.Load = function(form, options){
9468     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9469     this.reader = this.form.reader;
9470 };
9471
9472 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9473     type : 'load',
9474
9475     run : function(){
9476         
9477         Roo.Ajax.request(Roo.apply(
9478                 this.createCallback(), {
9479                     method:this.getMethod(),
9480                     url:this.getUrl(false),
9481                     params:this.getParams()
9482         }));
9483     },
9484
9485     success : function(response){
9486         
9487         var result = this.processResponse(response);
9488         if(result === true || !result.success || !result.data){
9489             this.failureType = Roo.form.Action.LOAD_FAILURE;
9490             this.form.afterAction(this, false);
9491             return;
9492         }
9493         this.form.clearInvalid();
9494         this.form.setValues(result.data);
9495         this.form.afterAction(this, true);
9496     },
9497
9498     handleResponse : function(response){
9499         if(this.form.reader){
9500             var rs = this.form.reader.read(response);
9501             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9502             return {
9503                 success : rs.success,
9504                 data : data
9505             };
9506         }
9507         return Roo.decode(response.responseText);
9508     }
9509 });
9510
9511 Roo.form.Action.ACTION_TYPES = {
9512     'load' : Roo.form.Action.Load,
9513     'submit' : Roo.form.Action.Submit
9514 };/*
9515  * - LGPL
9516  *
9517  * form
9518  *
9519  */
9520
9521 /**
9522  * @class Roo.bootstrap.Form
9523  * @extends Roo.bootstrap.Component
9524  * Bootstrap Form class
9525  * @cfg {String} method  GET | POST (default POST)
9526  * @cfg {String} labelAlign top | left (default top)
9527  * @cfg {String} align left  | right - for navbars
9528  * @cfg {Boolean} loadMask load mask when submit (default true)
9529
9530  *
9531  * @constructor
9532  * Create a new Form
9533  * @param {Object} config The config object
9534  */
9535
9536
9537 Roo.bootstrap.Form = function(config){
9538     
9539     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9540     
9541     Roo.bootstrap.Form.popover.apply();
9542     
9543     this.addEvents({
9544         /**
9545          * @event clientvalidation
9546          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9547          * @param {Form} this
9548          * @param {Boolean} valid true if the form has passed client-side validation
9549          */
9550         clientvalidation: true,
9551         /**
9552          * @event beforeaction
9553          * Fires before any action is performed. Return false to cancel the action.
9554          * @param {Form} this
9555          * @param {Action} action The action to be performed
9556          */
9557         beforeaction: true,
9558         /**
9559          * @event actionfailed
9560          * Fires when an action fails.
9561          * @param {Form} this
9562          * @param {Action} action The action that failed
9563          */
9564         actionfailed : true,
9565         /**
9566          * @event actioncomplete
9567          * Fires when an action is completed.
9568          * @param {Form} this
9569          * @param {Action} action The action that completed
9570          */
9571         actioncomplete : true
9572     });
9573 };
9574
9575 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9576
9577      /**
9578      * @cfg {String} method
9579      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9580      */
9581     method : 'POST',
9582     /**
9583      * @cfg {String} url
9584      * The URL to use for form actions if one isn't supplied in the action options.
9585      */
9586     /**
9587      * @cfg {Boolean} fileUpload
9588      * Set to true if this form is a file upload.
9589      */
9590
9591     /**
9592      * @cfg {Object} baseParams
9593      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9594      */
9595
9596     /**
9597      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9598      */
9599     timeout: 30,
9600     /**
9601      * @cfg {Sting} align (left|right) for navbar forms
9602      */
9603     align : 'left',
9604
9605     // private
9606     activeAction : null,
9607
9608     /**
9609      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9610      * element by passing it or its id or mask the form itself by passing in true.
9611      * @type Mixed
9612      */
9613     waitMsgTarget : false,
9614
9615     loadMask : true,
9616     
9617     /**
9618      * @cfg {Boolean} errorMask (true|false) default false
9619      */
9620     errorMask : false,
9621     
9622     /**
9623      * @cfg {Number} maskOffset Default 100
9624      */
9625     maskOffset : 100,
9626     
9627     /**
9628      * @cfg {Boolean} maskBody
9629      */
9630     maskBody : false,
9631
9632     getAutoCreate : function(){
9633
9634         var cfg = {
9635             tag: 'form',
9636             method : this.method || 'POST',
9637             id : this.id || Roo.id(),
9638             cls : ''
9639         };
9640         if (this.parent().xtype.match(/^Nav/)) {
9641             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9642
9643         }
9644
9645         if (this.labelAlign == 'left' ) {
9646             cfg.cls += ' form-horizontal';
9647         }
9648
9649
9650         return cfg;
9651     },
9652     initEvents : function()
9653     {
9654         this.el.on('submit', this.onSubmit, this);
9655         // this was added as random key presses on the form where triggering form submit.
9656         this.el.on('keypress', function(e) {
9657             if (e.getCharCode() != 13) {
9658                 return true;
9659             }
9660             // we might need to allow it for textareas.. and some other items.
9661             // check e.getTarget().
9662
9663             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9664                 return true;
9665             }
9666
9667             Roo.log("keypress blocked");
9668
9669             e.preventDefault();
9670             return false;
9671         });
9672         
9673     },
9674     // private
9675     onSubmit : function(e){
9676         e.stopEvent();
9677     },
9678
9679      /**
9680      * Returns true if client-side validation on the form is successful.
9681      * @return Boolean
9682      */
9683     isValid : function(){
9684         var items = this.getItems();
9685         var valid = true;
9686         var target = false;
9687         
9688         items.each(function(f){
9689             
9690             if(f.validate()){
9691                 return;
9692             }
9693             
9694             Roo.log('invalid field: ' + f.name);
9695             
9696             valid = false;
9697
9698             if(!target && f.el.isVisible(true)){
9699                 target = f;
9700             }
9701            
9702         });
9703         
9704         if(this.errorMask && !valid){
9705             Roo.bootstrap.Form.popover.mask(this, target);
9706         }
9707         
9708         return valid;
9709     },
9710     
9711     /**
9712      * Returns true if any fields in this form have changed since their original load.
9713      * @return Boolean
9714      */
9715     isDirty : function(){
9716         var dirty = false;
9717         var items = this.getItems();
9718         items.each(function(f){
9719            if(f.isDirty()){
9720                dirty = true;
9721                return false;
9722            }
9723            return true;
9724         });
9725         return dirty;
9726     },
9727      /**
9728      * Performs a predefined action (submit or load) or custom actions you define on this form.
9729      * @param {String} actionName The name of the action type
9730      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9731      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9732      * accept other config options):
9733      * <pre>
9734 Property          Type             Description
9735 ----------------  ---------------  ----------------------------------------------------------------------------------
9736 url               String           The url for the action (defaults to the form's url)
9737 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9738 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9739 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9740                                    validate the form on the client (defaults to false)
9741      * </pre>
9742      * @return {BasicForm} this
9743      */
9744     doAction : function(action, options){
9745         if(typeof action == 'string'){
9746             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9747         }
9748         if(this.fireEvent('beforeaction', this, action) !== false){
9749             this.beforeAction(action);
9750             action.run.defer(100, action);
9751         }
9752         return this;
9753     },
9754
9755     // private
9756     beforeAction : function(action){
9757         var o = action.options;
9758         
9759         if(this.loadMask){
9760             
9761             if(this.maskBody){
9762                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9763             } else {
9764                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9765             }
9766         }
9767         // not really supported yet.. ??
9768
9769         //if(this.waitMsgTarget === true){
9770         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9771         //}else if(this.waitMsgTarget){
9772         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9773         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774         //}else {
9775         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9776        // }
9777
9778     },
9779
9780     // private
9781     afterAction : function(action, success){
9782         this.activeAction = null;
9783         var o = action.options;
9784
9785         if(this.loadMask){
9786             
9787             if(this.maskBody){
9788                 Roo.get(document.body).unmask();
9789             } else {
9790                 this.el.unmask();
9791             }
9792         }
9793         
9794         //if(this.waitMsgTarget === true){
9795 //            this.el.unmask();
9796         //}else if(this.waitMsgTarget){
9797         //    this.waitMsgTarget.unmask();
9798         //}else{
9799         //    Roo.MessageBox.updateProgress(1);
9800         //    Roo.MessageBox.hide();
9801        // }
9802         //
9803         if(success){
9804             if(o.reset){
9805                 this.reset();
9806             }
9807             Roo.callback(o.success, o.scope, [this, action]);
9808             this.fireEvent('actioncomplete', this, action);
9809
9810         }else{
9811
9812             // failure condition..
9813             // we have a scenario where updates need confirming.
9814             // eg. if a locking scenario exists..
9815             // we look for { errors : { needs_confirm : true }} in the response.
9816             if (
9817                 (typeof(action.result) != 'undefined')  &&
9818                 (typeof(action.result.errors) != 'undefined')  &&
9819                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9820            ){
9821                 var _t = this;
9822                 Roo.log("not supported yet");
9823                  /*
9824
9825                 Roo.MessageBox.confirm(
9826                     "Change requires confirmation",
9827                     action.result.errorMsg,
9828                     function(r) {
9829                         if (r != 'yes') {
9830                             return;
9831                         }
9832                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9833                     }
9834
9835                 );
9836                 */
9837
9838
9839                 return;
9840             }
9841
9842             Roo.callback(o.failure, o.scope, [this, action]);
9843             // show an error message if no failed handler is set..
9844             if (!this.hasListener('actionfailed')) {
9845                 Roo.log("need to add dialog support");
9846                 /*
9847                 Roo.MessageBox.alert("Error",
9848                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9849                         action.result.errorMsg :
9850                         "Saving Failed, please check your entries or try again"
9851                 );
9852                 */
9853             }
9854
9855             this.fireEvent('actionfailed', this, action);
9856         }
9857
9858     },
9859     /**
9860      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9861      * @param {String} id The value to search for
9862      * @return Field
9863      */
9864     findField : function(id){
9865         var items = this.getItems();
9866         var field = items.get(id);
9867         if(!field){
9868              items.each(function(f){
9869                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9870                     field = f;
9871                     return false;
9872                 }
9873                 return true;
9874             });
9875         }
9876         return field || null;
9877     },
9878      /**
9879      * Mark fields in this form invalid in bulk.
9880      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9881      * @return {BasicForm} this
9882      */
9883     markInvalid : function(errors){
9884         if(errors instanceof Array){
9885             for(var i = 0, len = errors.length; i < len; i++){
9886                 var fieldError = errors[i];
9887                 var f = this.findField(fieldError.id);
9888                 if(f){
9889                     f.markInvalid(fieldError.msg);
9890                 }
9891             }
9892         }else{
9893             var field, id;
9894             for(id in errors){
9895                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9896                     field.markInvalid(errors[id]);
9897                 }
9898             }
9899         }
9900         //Roo.each(this.childForms || [], function (f) {
9901         //    f.markInvalid(errors);
9902         //});
9903
9904         return this;
9905     },
9906
9907     /**
9908      * Set values for fields in this form in bulk.
9909      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9910      * @return {BasicForm} this
9911      */
9912     setValues : function(values){
9913         if(values instanceof Array){ // array of objects
9914             for(var i = 0, len = values.length; i < len; i++){
9915                 var v = values[i];
9916                 var f = this.findField(v.id);
9917                 if(f){
9918                     f.setValue(v.value);
9919                     if(this.trackResetOnLoad){
9920                         f.originalValue = f.getValue();
9921                     }
9922                 }
9923             }
9924         }else{ // object hash
9925             var field, id;
9926             for(id in values){
9927                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9928
9929                     if (field.setFromData &&
9930                         field.valueField &&
9931                         field.displayField &&
9932                         // combos' with local stores can
9933                         // be queried via setValue()
9934                         // to set their value..
9935                         (field.store && !field.store.isLocal)
9936                         ) {
9937                         // it's a combo
9938                         var sd = { };
9939                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9940                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9941                         field.setFromData(sd);
9942
9943                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9944                         
9945                         field.setFromData(values);
9946                         
9947                     } else {
9948                         field.setValue(values[id]);
9949                     }
9950
9951
9952                     if(this.trackResetOnLoad){
9953                         field.originalValue = field.getValue();
9954                     }
9955                 }
9956             }
9957         }
9958
9959         //Roo.each(this.childForms || [], function (f) {
9960         //    f.setValues(values);
9961         //});
9962
9963         return this;
9964     },
9965
9966     /**
9967      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9968      * they are returned as an array.
9969      * @param {Boolean} asString
9970      * @return {Object}
9971      */
9972     getValues : function(asString){
9973         //if (this.childForms) {
9974             // copy values from the child forms
9975         //    Roo.each(this.childForms, function (f) {
9976         //        this.setValues(f.getValues());
9977         //    }, this);
9978         //}
9979
9980
9981
9982         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9983         if(asString === true){
9984             return fs;
9985         }
9986         return Roo.urlDecode(fs);
9987     },
9988
9989     /**
9990      * Returns the fields in this form as an object with key/value pairs.
9991      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9992      * @return {Object}
9993      */
9994     getFieldValues : function(with_hidden)
9995     {
9996         var items = this.getItems();
9997         var ret = {};
9998         items.each(function(f){
9999             
10000             if (!f.getName()) {
10001                 return;
10002             }
10003             
10004             var v = f.getValue();
10005             
10006             if (f.inputType =='radio') {
10007                 if (typeof(ret[f.getName()]) == 'undefined') {
10008                     ret[f.getName()] = ''; // empty..
10009                 }
10010
10011                 if (!f.el.dom.checked) {
10012                     return;
10013
10014                 }
10015                 v = f.el.dom.value;
10016
10017             }
10018             
10019             if(f.xtype == 'MoneyField'){
10020                 ret[f.currencyName] = f.getCurrency();
10021             }
10022
10023             // not sure if this supported any more..
10024             if ((typeof(v) == 'object') && f.getRawValue) {
10025                 v = f.getRawValue() ; // dates..
10026             }
10027             // combo boxes where name != hiddenName...
10028             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10029                 ret[f.name] = f.getRawValue();
10030             }
10031             ret[f.getName()] = v;
10032         });
10033
10034         return ret;
10035     },
10036
10037     /**
10038      * Clears all invalid messages in this form.
10039      * @return {BasicForm} this
10040      */
10041     clearInvalid : function(){
10042         var items = this.getItems();
10043
10044         items.each(function(f){
10045            f.clearInvalid();
10046         });
10047
10048         return this;
10049     },
10050
10051     /**
10052      * Resets this form.
10053      * @return {BasicForm} this
10054      */
10055     reset : function(){
10056         var items = this.getItems();
10057         items.each(function(f){
10058             f.reset();
10059         });
10060
10061         Roo.each(this.childForms || [], function (f) {
10062             f.reset();
10063         });
10064
10065
10066         return this;
10067     },
10068     
10069     getItems : function()
10070     {
10071         var r=new Roo.util.MixedCollection(false, function(o){
10072             return o.id || (o.id = Roo.id());
10073         });
10074         var iter = function(el) {
10075             if (el.inputEl) {
10076                 r.add(el);
10077             }
10078             if (!el.items) {
10079                 return;
10080             }
10081             Roo.each(el.items,function(e) {
10082                 iter(e);
10083             });
10084         };
10085
10086         iter(this);
10087         return r;
10088     },
10089     
10090     hideFields : function(items)
10091     {
10092         Roo.each(items, function(i){
10093             
10094             var f = this.findField(i);
10095             
10096             if(!f){
10097                 return;
10098             }
10099             
10100             f.hide();
10101             
10102         }, this);
10103     },
10104     
10105     showFields : function(items)
10106     {
10107         Roo.each(items, function(i){
10108             
10109             var f = this.findField(i);
10110             
10111             if(!f){
10112                 return;
10113             }
10114             
10115             f.show();
10116             
10117         }, this);
10118     }
10119
10120 });
10121
10122 Roo.apply(Roo.bootstrap.Form, {
10123     
10124     popover : {
10125         
10126         padding : 5,
10127         
10128         isApplied : false,
10129         
10130         isMasked : false,
10131         
10132         form : false,
10133         
10134         target : false,
10135         
10136         toolTip : false,
10137         
10138         intervalID : false,
10139         
10140         maskEl : false,
10141         
10142         apply : function()
10143         {
10144             if(this.isApplied){
10145                 return;
10146             }
10147             
10148             this.maskEl = {
10149                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10150                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10151                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10152                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10153             };
10154             
10155             this.maskEl.top.enableDisplayMode("block");
10156             this.maskEl.left.enableDisplayMode("block");
10157             this.maskEl.bottom.enableDisplayMode("block");
10158             this.maskEl.right.enableDisplayMode("block");
10159             
10160             this.toolTip = new Roo.bootstrap.Tooltip({
10161                 cls : 'roo-form-error-popover',
10162                 alignment : {
10163                     'left' : ['r-l', [-2,0], 'right'],
10164                     'right' : ['l-r', [2,0], 'left'],
10165                     'bottom' : ['tl-bl', [0,2], 'top'],
10166                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10167                 }
10168             });
10169             
10170             this.toolTip.render(Roo.get(document.body));
10171
10172             this.toolTip.el.enableDisplayMode("block");
10173             
10174             Roo.get(document.body).on('click', function(){
10175                 this.unmask();
10176             }, this);
10177             
10178             Roo.get(document.body).on('touchstart', function(){
10179                 this.unmask();
10180             }, this);
10181             
10182             this.isApplied = true
10183         },
10184         
10185         mask : function(form, target)
10186         {
10187             this.form = form;
10188             
10189             this.target = target;
10190             
10191             if(!this.form.errorMask || !target.el){
10192                 return;
10193             }
10194             
10195             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10196             
10197             Roo.log(scrollable);
10198             
10199             var ot = this.target.el.calcOffsetsTo(scrollable);
10200             
10201             var scrollTo = ot[1] - this.form.maskOffset;
10202             
10203             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10204             
10205             scrollable.scrollTo('top', scrollTo);
10206             
10207             var box = this.target.el.getBox();
10208             Roo.log(box);
10209             var zIndex = Roo.bootstrap.Modal.zIndex++;
10210
10211             
10212             this.maskEl.top.setStyle('position', 'absolute');
10213             this.maskEl.top.setStyle('z-index', zIndex);
10214             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10215             this.maskEl.top.setLeft(0);
10216             this.maskEl.top.setTop(0);
10217             this.maskEl.top.show();
10218             
10219             this.maskEl.left.setStyle('position', 'absolute');
10220             this.maskEl.left.setStyle('z-index', zIndex);
10221             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10222             this.maskEl.left.setLeft(0);
10223             this.maskEl.left.setTop(box.y - this.padding);
10224             this.maskEl.left.show();
10225
10226             this.maskEl.bottom.setStyle('position', 'absolute');
10227             this.maskEl.bottom.setStyle('z-index', zIndex);
10228             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10229             this.maskEl.bottom.setLeft(0);
10230             this.maskEl.bottom.setTop(box.bottom + this.padding);
10231             this.maskEl.bottom.show();
10232
10233             this.maskEl.right.setStyle('position', 'absolute');
10234             this.maskEl.right.setStyle('z-index', zIndex);
10235             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10236             this.maskEl.right.setLeft(box.right + this.padding);
10237             this.maskEl.right.setTop(box.y - this.padding);
10238             this.maskEl.right.show();
10239
10240             this.toolTip.bindEl = this.target.el;
10241
10242             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10243
10244             var tip = this.target.blankText;
10245
10246             if(this.target.getValue() !== '' ) {
10247                 
10248                 if (this.target.invalidText.length) {
10249                     tip = this.target.invalidText;
10250                 } else if (this.target.regexText.length){
10251                     tip = this.target.regexText;
10252                 }
10253             }
10254
10255             this.toolTip.show(tip);
10256
10257             this.intervalID = window.setInterval(function() {
10258                 Roo.bootstrap.Form.popover.unmask();
10259             }, 10000);
10260
10261             window.onwheel = function(){ return false;};
10262             
10263             (function(){ this.isMasked = true; }).defer(500, this);
10264             
10265         },
10266         
10267         unmask : function()
10268         {
10269             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10270                 return;
10271             }
10272             
10273             this.maskEl.top.setStyle('position', 'absolute');
10274             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10275             this.maskEl.top.hide();
10276
10277             this.maskEl.left.setStyle('position', 'absolute');
10278             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10279             this.maskEl.left.hide();
10280
10281             this.maskEl.bottom.setStyle('position', 'absolute');
10282             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10283             this.maskEl.bottom.hide();
10284
10285             this.maskEl.right.setStyle('position', 'absolute');
10286             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10287             this.maskEl.right.hide();
10288             
10289             this.toolTip.hide();
10290             
10291             this.toolTip.el.hide();
10292             
10293             window.onwheel = function(){ return true;};
10294             
10295             if(this.intervalID){
10296                 window.clearInterval(this.intervalID);
10297                 this.intervalID = false;
10298             }
10299             
10300             this.isMasked = false;
10301             
10302         }
10303         
10304     }
10305     
10306 });
10307
10308 /*
10309  * Based on:
10310  * Ext JS Library 1.1.1
10311  * Copyright(c) 2006-2007, Ext JS, LLC.
10312  *
10313  * Originally Released Under LGPL - original licence link has changed is not relivant.
10314  *
10315  * Fork - LGPL
10316  * <script type="text/javascript">
10317  */
10318 /**
10319  * @class Roo.form.VTypes
10320  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10321  * @singleton
10322  */
10323 Roo.form.VTypes = function(){
10324     // closure these in so they are only created once.
10325     var alpha = /^[a-zA-Z_]+$/;
10326     var alphanum = /^[a-zA-Z0-9_]+$/;
10327     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10328     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10329
10330     // All these messages and functions are configurable
10331     return {
10332         /**
10333          * The function used to validate email addresses
10334          * @param {String} value The email address
10335          */
10336         'email' : function(v){
10337             return email.test(v);
10338         },
10339         /**
10340          * The error text to display when the email validation function returns false
10341          * @type String
10342          */
10343         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10344         /**
10345          * The keystroke filter mask to be applied on email input
10346          * @type RegExp
10347          */
10348         'emailMask' : /[a-z0-9_\.\-@]/i,
10349
10350         /**
10351          * The function used to validate URLs
10352          * @param {String} value The URL
10353          */
10354         'url' : function(v){
10355             return url.test(v);
10356         },
10357         /**
10358          * The error text to display when the url validation function returns false
10359          * @type String
10360          */
10361         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10362         
10363         /**
10364          * The function used to validate alpha values
10365          * @param {String} value The value
10366          */
10367         'alpha' : function(v){
10368             return alpha.test(v);
10369         },
10370         /**
10371          * The error text to display when the alpha validation function returns false
10372          * @type String
10373          */
10374         'alphaText' : 'This field should only contain letters and _',
10375         /**
10376          * The keystroke filter mask to be applied on alpha input
10377          * @type RegExp
10378          */
10379         'alphaMask' : /[a-z_]/i,
10380
10381         /**
10382          * The function used to validate alphanumeric values
10383          * @param {String} value The value
10384          */
10385         'alphanum' : function(v){
10386             return alphanum.test(v);
10387         },
10388         /**
10389          * The error text to display when the alphanumeric validation function returns false
10390          * @type String
10391          */
10392         'alphanumText' : 'This field should only contain letters, numbers and _',
10393         /**
10394          * The keystroke filter mask to be applied on alphanumeric input
10395          * @type RegExp
10396          */
10397         'alphanumMask' : /[a-z0-9_]/i
10398     };
10399 }();/*
10400  * - LGPL
10401  *
10402  * Input
10403  * 
10404  */
10405
10406 /**
10407  * @class Roo.bootstrap.Input
10408  * @extends Roo.bootstrap.Component
10409  * Bootstrap Input class
10410  * @cfg {Boolean} disabled is it disabled
10411  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10412  * @cfg {String} name name of the input
10413  * @cfg {string} fieldLabel - the label associated
10414  * @cfg {string} placeholder - placeholder to put in text.
10415  * @cfg {string}  before - input group add on before
10416  * @cfg {string} after - input group add on after
10417  * @cfg {string} size - (lg|sm) or leave empty..
10418  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10419  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10420  * @cfg {Number} md colspan out of 12 for computer-sized screens
10421  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10422  * @cfg {string} value default value of the input
10423  * @cfg {Number} labelWidth set the width of label 
10424  * @cfg {Number} labellg set the width of label (1-12)
10425  * @cfg {Number} labelmd set the width of label (1-12)
10426  * @cfg {Number} labelsm set the width of label (1-12)
10427  * @cfg {Number} labelxs set the width of label (1-12)
10428  * @cfg {String} labelAlign (top|left)
10429  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10430  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10431  * @cfg {String} indicatorpos (left|right) default left
10432  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10433  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10434  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10435
10436  * @cfg {String} align (left|center|right) Default left
10437  * @cfg {Boolean} forceFeedback (true|false) Default false
10438  * 
10439  * @constructor
10440  * Create a new Input
10441  * @param {Object} config The config object
10442  */
10443
10444 Roo.bootstrap.Input = function(config){
10445     
10446     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10447     
10448     this.addEvents({
10449         /**
10450          * @event focus
10451          * Fires when this field receives input focus.
10452          * @param {Roo.form.Field} this
10453          */
10454         focus : true,
10455         /**
10456          * @event blur
10457          * Fires when this field loses input focus.
10458          * @param {Roo.form.Field} this
10459          */
10460         blur : true,
10461         /**
10462          * @event specialkey
10463          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10464          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10465          * @param {Roo.form.Field} this
10466          * @param {Roo.EventObject} e The event object
10467          */
10468         specialkey : true,
10469         /**
10470          * @event change
10471          * Fires just before the field blurs if the field value has changed.
10472          * @param {Roo.form.Field} this
10473          * @param {Mixed} newValue The new value
10474          * @param {Mixed} oldValue The original value
10475          */
10476         change : true,
10477         /**
10478          * @event invalid
10479          * Fires after the field has been marked as invalid.
10480          * @param {Roo.form.Field} this
10481          * @param {String} msg The validation message
10482          */
10483         invalid : true,
10484         /**
10485          * @event valid
10486          * Fires after the field has been validated with no errors.
10487          * @param {Roo.form.Field} this
10488          */
10489         valid : true,
10490          /**
10491          * @event keyup
10492          * Fires after the key up
10493          * @param {Roo.form.Field} this
10494          * @param {Roo.EventObject}  e The event Object
10495          */
10496         keyup : true
10497     });
10498 };
10499
10500 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10501      /**
10502      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10503       automatic validation (defaults to "keyup").
10504      */
10505     validationEvent : "keyup",
10506      /**
10507      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10508      */
10509     validateOnBlur : true,
10510     /**
10511      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10512      */
10513     validationDelay : 250,
10514      /**
10515      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10516      */
10517     focusClass : "x-form-focus",  // not needed???
10518     
10519        
10520     /**
10521      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     invalidClass : "has-warning",
10524     
10525     /**
10526      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10527      */
10528     validClass : "has-success",
10529     
10530     /**
10531      * @cfg {Boolean} hasFeedback (true|false) default true
10532      */
10533     hasFeedback : true,
10534     
10535     /**
10536      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     invalidFeedbackClass : "glyphicon-warning-sign",
10539     
10540     /**
10541      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10542      */
10543     validFeedbackClass : "glyphicon-ok",
10544     
10545     /**
10546      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10547      */
10548     selectOnFocus : false,
10549     
10550      /**
10551      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10552      */
10553     maskRe : null,
10554        /**
10555      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10556      */
10557     vtype : null,
10558     
10559       /**
10560      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10561      */
10562     disableKeyFilter : false,
10563     
10564        /**
10565      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10566      */
10567     disabled : false,
10568      /**
10569      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10570      */
10571     allowBlank : true,
10572     /**
10573      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10574      */
10575     blankText : "Please complete this mandatory field",
10576     
10577      /**
10578      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10579      */
10580     minLength : 0,
10581     /**
10582      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10583      */
10584     maxLength : Number.MAX_VALUE,
10585     /**
10586      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10587      */
10588     minLengthText : "The minimum length for this field is {0}",
10589     /**
10590      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10591      */
10592     maxLengthText : "The maximum length for this field is {0}",
10593   
10594     
10595     /**
10596      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10597      * If available, this function will be called only after the basic validators all return true, and will be passed the
10598      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10599      */
10600     validator : null,
10601     /**
10602      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10603      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10604      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10605      */
10606     regex : null,
10607     /**
10608      * @cfg {String} regexText -- Depricated - use Invalid Text
10609      */
10610     regexText : "",
10611     
10612     /**
10613      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10614      */
10615     invalidText : "",
10616     
10617     
10618     
10619     autocomplete: false,
10620     
10621     
10622     fieldLabel : '',
10623     inputType : 'text',
10624     
10625     name : false,
10626     placeholder: false,
10627     before : false,
10628     after : false,
10629     size : false,
10630     hasFocus : false,
10631     preventMark: false,
10632     isFormField : true,
10633     value : '',
10634     labelWidth : 2,
10635     labelAlign : false,
10636     readOnly : false,
10637     align : false,
10638     formatedValue : false,
10639     forceFeedback : false,
10640     
10641     indicatorpos : 'left',
10642     
10643     labellg : 0,
10644     labelmd : 0,
10645     labelsm : 0,
10646     labelxs : 0,
10647     
10648     capture : '',
10649     accept : '',
10650     
10651     parentLabelAlign : function()
10652     {
10653         var parent = this;
10654         while (parent.parent()) {
10655             parent = parent.parent();
10656             if (typeof(parent.labelAlign) !='undefined') {
10657                 return parent.labelAlign;
10658             }
10659         }
10660         return 'left';
10661         
10662     },
10663     
10664     getAutoCreate : function()
10665     {
10666         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10667         
10668         var id = Roo.id();
10669         
10670         var cfg = {};
10671         
10672         if(this.inputType != 'hidden'){
10673             cfg.cls = 'form-group' //input-group
10674         }
10675         
10676         var input =  {
10677             tag: 'input',
10678             id : id,
10679             type : this.inputType,
10680             value : this.value,
10681             cls : 'form-control',
10682             placeholder : this.placeholder || '',
10683             autocomplete : this.autocomplete || 'new-password'
10684         };
10685         if (this.inputType == 'file') {
10686             input.style = 'overflow:hidden'; // why not in CSS?
10687         }
10688         
10689         if(this.capture.length){
10690             input.capture = this.capture;
10691         }
10692         
10693         if(this.accept.length){
10694             input.accept = this.accept + "/*";
10695         }
10696         
10697         if(this.align){
10698             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10699         }
10700         
10701         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10702             input.maxLength = this.maxLength;
10703         }
10704         
10705         if (this.disabled) {
10706             input.disabled=true;
10707         }
10708         
10709         if (this.readOnly) {
10710             input.readonly=true;
10711         }
10712         
10713         if (this.name) {
10714             input.name = this.name;
10715         }
10716         
10717         if (this.size) {
10718             input.cls += ' input-' + this.size;
10719         }
10720         
10721         var settings=this;
10722         ['xs','sm','md','lg'].map(function(size){
10723             if (settings[size]) {
10724                 cfg.cls += ' col-' + size + '-' + settings[size];
10725             }
10726         });
10727         
10728         var inputblock = input;
10729         
10730         var feedback = {
10731             tag: 'span',
10732             cls: 'glyphicon form-control-feedback'
10733         };
10734             
10735         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10736             
10737             inputblock = {
10738                 cls : 'has-feedback',
10739                 cn :  [
10740                     input,
10741                     feedback
10742                 ] 
10743             };  
10744         }
10745         
10746         if (this.before || this.after) {
10747             
10748             inputblock = {
10749                 cls : 'input-group',
10750                 cn :  [] 
10751             };
10752             
10753             if (this.before && typeof(this.before) == 'string') {
10754                 
10755                 inputblock.cn.push({
10756                     tag :'span',
10757                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10758                     html : this.before
10759                 });
10760             }
10761             if (this.before && typeof(this.before) == 'object') {
10762                 this.before = Roo.factory(this.before);
10763                 
10764                 inputblock.cn.push({
10765                     tag :'span',
10766                     cls : 'roo-input-before input-group-prepend   input-group-' +
10767                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10768                 });
10769             }
10770             
10771             inputblock.cn.push(input);
10772             
10773             if (this.after && typeof(this.after) == 'string') {
10774                 inputblock.cn.push({
10775                     tag :'span',
10776                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10777                     html : this.after
10778                 });
10779             }
10780             if (this.after && typeof(this.after) == 'object') {
10781                 this.after = Roo.factory(this.after);
10782                 
10783                 inputblock.cn.push({
10784                     tag :'span',
10785                     cls : 'roo-input-after input-group-append  input-group-' +
10786                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10787                 });
10788             }
10789             
10790             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10791                 inputblock.cls += ' has-feedback';
10792                 inputblock.cn.push(feedback);
10793             }
10794         };
10795         var indicator = {
10796             tag : 'i',
10797             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10798             tooltip : 'This field is required'
10799         };
10800         if (this.allowBlank ) {
10801             indicator.style = this.allowBlank ? ' display:none' : '';
10802         }
10803         if (align ==='left' && this.fieldLabel.length) {
10804             
10805             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10806             
10807             cfg.cn = [
10808                 indicator,
10809                 {
10810                     tag: 'label',
10811                     'for' :  id,
10812                     cls : 'control-label col-form-label',
10813                     html : this.fieldLabel
10814
10815                 },
10816                 {
10817                     cls : "", 
10818                     cn: [
10819                         inputblock
10820                     ]
10821                 }
10822             ];
10823             
10824             var labelCfg = cfg.cn[1];
10825             var contentCfg = cfg.cn[2];
10826             
10827             if(this.indicatorpos == 'right'){
10828                 cfg.cn = [
10829                     {
10830                         tag: 'label',
10831                         'for' :  id,
10832                         cls : 'control-label col-form-label',
10833                         cn : [
10834                             {
10835                                 tag : 'span',
10836                                 html : this.fieldLabel
10837                             },
10838                             indicator
10839                         ]
10840                     },
10841                     {
10842                         cls : "",
10843                         cn: [
10844                             inputblock
10845                         ]
10846                     }
10847
10848                 ];
10849                 
10850                 labelCfg = cfg.cn[0];
10851                 contentCfg = cfg.cn[1];
10852             
10853             }
10854             
10855             if(this.labelWidth > 12){
10856                 labelCfg.style = "width: " + this.labelWidth + 'px';
10857             }
10858             
10859             if(this.labelWidth < 13 && this.labelmd == 0){
10860                 this.labelmd = this.labelWidth;
10861             }
10862             
10863             if(this.labellg > 0){
10864                 labelCfg.cls += ' col-lg-' + this.labellg;
10865                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10866             }
10867             
10868             if(this.labelmd > 0){
10869                 labelCfg.cls += ' col-md-' + this.labelmd;
10870                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10871             }
10872             
10873             if(this.labelsm > 0){
10874                 labelCfg.cls += ' col-sm-' + this.labelsm;
10875                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10876             }
10877             
10878             if(this.labelxs > 0){
10879                 labelCfg.cls += ' col-xs-' + this.labelxs;
10880                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10881             }
10882             
10883             
10884         } else if ( this.fieldLabel.length) {
10885                 
10886             
10887             
10888             cfg.cn = [
10889                 {
10890                     tag : 'i',
10891                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10892                     tooltip : 'This field is required',
10893                     style : this.allowBlank ? ' display:none' : '' 
10894                 },
10895                 {
10896                     tag: 'label',
10897                    //cls : 'input-group-addon',
10898                     html : this.fieldLabel
10899
10900                 },
10901
10902                inputblock
10903
10904            ];
10905            
10906            if(this.indicatorpos == 'right'){
10907        
10908                 cfg.cn = [
10909                     {
10910                         tag: 'label',
10911                        //cls : 'input-group-addon',
10912                         html : this.fieldLabel
10913
10914                     },
10915                     {
10916                         tag : 'i',
10917                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10918                         tooltip : 'This field is required',
10919                         style : this.allowBlank ? ' display:none' : '' 
10920                     },
10921
10922                    inputblock
10923
10924                ];
10925
10926             }
10927
10928         } else {
10929             
10930             cfg.cn = [
10931
10932                     inputblock
10933
10934             ];
10935                 
10936                 
10937         };
10938         
10939         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10940            cfg.cls += ' navbar-form';
10941         }
10942         
10943         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10944             // on BS4 we do this only if not form 
10945             cfg.cls += ' navbar-form';
10946             cfg.tag = 'li';
10947         }
10948         
10949         return cfg;
10950         
10951     },
10952     /**
10953      * return the real input element.
10954      */
10955     inputEl: function ()
10956     {
10957         return this.el.select('input.form-control',true).first();
10958     },
10959     
10960     tooltipEl : function()
10961     {
10962         return this.inputEl();
10963     },
10964     
10965     indicatorEl : function()
10966     {
10967         if (Roo.bootstrap.version == 4) {
10968             return false; // not enabled in v4 yet.
10969         }
10970         
10971         var indicator = this.el.select('i.roo-required-indicator',true).first();
10972         
10973         if(!indicator){
10974             return false;
10975         }
10976         
10977         return indicator;
10978         
10979     },
10980     
10981     setDisabled : function(v)
10982     {
10983         var i  = this.inputEl().dom;
10984         if (!v) {
10985             i.removeAttribute('disabled');
10986             return;
10987             
10988         }
10989         i.setAttribute('disabled','true');
10990     },
10991     initEvents : function()
10992     {
10993           
10994         this.inputEl().on("keydown" , this.fireKey,  this);
10995         this.inputEl().on("focus", this.onFocus,  this);
10996         this.inputEl().on("blur", this.onBlur,  this);
10997         
10998         this.inputEl().relayEvent('keyup', this);
10999         
11000         this.indicator = this.indicatorEl();
11001         
11002         if(this.indicator){
11003             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11004         }
11005  
11006         // reference to original value for reset
11007         this.originalValue = this.getValue();
11008         //Roo.form.TextField.superclass.initEvents.call(this);
11009         if(this.validationEvent == 'keyup'){
11010             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11011             this.inputEl().on('keyup', this.filterValidation, this);
11012         }
11013         else if(this.validationEvent !== false){
11014             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11015         }
11016         
11017         if(this.selectOnFocus){
11018             this.on("focus", this.preFocus, this);
11019             
11020         }
11021         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11022             this.inputEl().on("keypress", this.filterKeys, this);
11023         } else {
11024             this.inputEl().relayEvent('keypress', this);
11025         }
11026        /* if(this.grow){
11027             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11028             this.el.on("click", this.autoSize,  this);
11029         }
11030         */
11031         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11032             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11033         }
11034         
11035         if (typeof(this.before) == 'object') {
11036             this.before.render(this.el.select('.roo-input-before',true).first());
11037         }
11038         if (typeof(this.after) == 'object') {
11039             this.after.render(this.el.select('.roo-input-after',true).first());
11040         }
11041         
11042         this.inputEl().on('change', this.onChange, this);
11043         
11044     },
11045     filterValidation : function(e){
11046         if(!e.isNavKeyPress()){
11047             this.validationTask.delay(this.validationDelay);
11048         }
11049     },
11050      /**
11051      * Validates the field value
11052      * @return {Boolean} True if the value is valid, else false
11053      */
11054     validate : function(){
11055         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11056         if(this.disabled || this.validateValue(this.getRawValue())){
11057             this.markValid();
11058             return true;
11059         }
11060         
11061         this.markInvalid();
11062         return false;
11063     },
11064     
11065     
11066     /**
11067      * Validates a value according to the field's validation rules and marks the field as invalid
11068      * if the validation fails
11069      * @param {Mixed} value The value to validate
11070      * @return {Boolean} True if the value is valid, else false
11071      */
11072     validateValue : function(value)
11073     {
11074         if(this.getVisibilityEl().hasClass('hidden')){
11075             return true;
11076         }
11077         
11078         if(value.length < 1)  { // if it's blank
11079             if(this.allowBlank){
11080                 return true;
11081             }
11082             return false;
11083         }
11084         
11085         if(value.length < this.minLength){
11086             return false;
11087         }
11088         if(value.length > this.maxLength){
11089             return false;
11090         }
11091         if(this.vtype){
11092             var vt = Roo.form.VTypes;
11093             if(!vt[this.vtype](value, this)){
11094                 return false;
11095             }
11096         }
11097         if(typeof this.validator == "function"){
11098             var msg = this.validator(value);
11099             if(msg !== true){
11100                 return false;
11101             }
11102             if (typeof(msg) == 'string') {
11103                 this.invalidText = msg;
11104             }
11105         }
11106         
11107         if(this.regex && !this.regex.test(value)){
11108             return false;
11109         }
11110         
11111         return true;
11112     },
11113     
11114      // private
11115     fireKey : function(e){
11116         //Roo.log('field ' + e.getKey());
11117         if(e.isNavKeyPress()){
11118             this.fireEvent("specialkey", this, e);
11119         }
11120     },
11121     focus : function (selectText){
11122         if(this.rendered){
11123             this.inputEl().focus();
11124             if(selectText === true){
11125                 this.inputEl().dom.select();
11126             }
11127         }
11128         return this;
11129     } ,
11130     
11131     onFocus : function(){
11132         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11133            // this.el.addClass(this.focusClass);
11134         }
11135         if(!this.hasFocus){
11136             this.hasFocus = true;
11137             this.startValue = this.getValue();
11138             this.fireEvent("focus", this);
11139         }
11140     },
11141     
11142     beforeBlur : Roo.emptyFn,
11143
11144     
11145     // private
11146     onBlur : function(){
11147         this.beforeBlur();
11148         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11149             //this.el.removeClass(this.focusClass);
11150         }
11151         this.hasFocus = false;
11152         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11153             this.validate();
11154         }
11155         var v = this.getValue();
11156         if(String(v) !== String(this.startValue)){
11157             this.fireEvent('change', this, v, this.startValue);
11158         }
11159         this.fireEvent("blur", this);
11160     },
11161     
11162     onChange : function(e)
11163     {
11164         var v = this.getValue();
11165         if(String(v) !== String(this.startValue)){
11166             this.fireEvent('change', this, v, this.startValue);
11167         }
11168         
11169     },
11170     
11171     /**
11172      * Resets the current field value to the originally loaded value and clears any validation messages
11173      */
11174     reset : function(){
11175         this.setValue(this.originalValue);
11176         this.validate();
11177     },
11178      /**
11179      * Returns the name of the field
11180      * @return {Mixed} name The name field
11181      */
11182     getName: function(){
11183         return this.name;
11184     },
11185      /**
11186      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11187      * @return {Mixed} value The field value
11188      */
11189     getValue : function(){
11190         
11191         var v = this.inputEl().getValue();
11192         
11193         return v;
11194     },
11195     /**
11196      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11197      * @return {Mixed} value The field value
11198      */
11199     getRawValue : function(){
11200         var v = this.inputEl().getValue();
11201         
11202         return v;
11203     },
11204     
11205     /**
11206      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11207      * @param {Mixed} value The value to set
11208      */
11209     setRawValue : function(v){
11210         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11211     },
11212     
11213     selectText : function(start, end){
11214         var v = this.getRawValue();
11215         if(v.length > 0){
11216             start = start === undefined ? 0 : start;
11217             end = end === undefined ? v.length : end;
11218             var d = this.inputEl().dom;
11219             if(d.setSelectionRange){
11220                 d.setSelectionRange(start, end);
11221             }else if(d.createTextRange){
11222                 var range = d.createTextRange();
11223                 range.moveStart("character", start);
11224                 range.moveEnd("character", v.length-end);
11225                 range.select();
11226             }
11227         }
11228     },
11229     
11230     /**
11231      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11232      * @param {Mixed} value The value to set
11233      */
11234     setValue : function(v){
11235         this.value = v;
11236         if(this.rendered){
11237             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11238             this.validate();
11239         }
11240     },
11241     
11242     /*
11243     processValue : function(value){
11244         if(this.stripCharsRe){
11245             var newValue = value.replace(this.stripCharsRe, '');
11246             if(newValue !== value){
11247                 this.setRawValue(newValue);
11248                 return newValue;
11249             }
11250         }
11251         return value;
11252     },
11253   */
11254     preFocus : function(){
11255         
11256         if(this.selectOnFocus){
11257             this.inputEl().dom.select();
11258         }
11259     },
11260     filterKeys : function(e){
11261         var k = e.getKey();
11262         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11263             return;
11264         }
11265         var c = e.getCharCode(), cc = String.fromCharCode(c);
11266         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11267             return;
11268         }
11269         if(!this.maskRe.test(cc)){
11270             e.stopEvent();
11271         }
11272     },
11273      /**
11274      * Clear any invalid styles/messages for this field
11275      */
11276     clearInvalid : function(){
11277         
11278         if(!this.el || this.preventMark){ // not rendered
11279             return;
11280         }
11281         
11282         
11283         this.el.removeClass([this.invalidClass, 'is-invalid']);
11284         
11285         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11286             
11287             var feedback = this.el.select('.form-control-feedback', true).first();
11288             
11289             if(feedback){
11290                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11291             }
11292             
11293         }
11294         
11295         if(this.indicator){
11296             this.indicator.removeClass('visible');
11297             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11298         }
11299         
11300         this.fireEvent('valid', this);
11301     },
11302     
11303      /**
11304      * Mark this field as valid
11305      */
11306     markValid : function()
11307     {
11308         if(!this.el  || this.preventMark){ // not rendered...
11309             return;
11310         }
11311         
11312         this.el.removeClass([this.invalidClass, this.validClass]);
11313         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11314
11315         var feedback = this.el.select('.form-control-feedback', true).first();
11316             
11317         if(feedback){
11318             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11319         }
11320         
11321         if(this.indicator){
11322             this.indicator.removeClass('visible');
11323             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11324         }
11325         
11326         if(this.disabled){
11327             return;
11328         }
11329         
11330            
11331         if(this.allowBlank && !this.getRawValue().length){
11332             return;
11333         }
11334         if (Roo.bootstrap.version == 3) {
11335             this.el.addClass(this.validClass);
11336         } else {
11337             this.inputEl().addClass('is-valid');
11338         }
11339
11340         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11341             
11342             var feedback = this.el.select('.form-control-feedback', true).first();
11343             
11344             if(feedback){
11345                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11346                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11347             }
11348             
11349         }
11350         
11351         this.fireEvent('valid', this);
11352     },
11353     
11354      /**
11355      * Mark this field as invalid
11356      * @param {String} msg The validation message
11357      */
11358     markInvalid : function(msg)
11359     {
11360         if(!this.el  || this.preventMark){ // not rendered
11361             return;
11362         }
11363         
11364         this.el.removeClass([this.invalidClass, this.validClass]);
11365         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11366         
11367         var feedback = this.el.select('.form-control-feedback', true).first();
11368             
11369         if(feedback){
11370             this.el.select('.form-control-feedback', true).first().removeClass(
11371                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11372         }
11373
11374         if(this.disabled){
11375             return;
11376         }
11377         
11378         if(this.allowBlank && !this.getRawValue().length){
11379             return;
11380         }
11381         
11382         if(this.indicator){
11383             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11384             this.indicator.addClass('visible');
11385         }
11386         if (Roo.bootstrap.version == 3) {
11387             this.el.addClass(this.invalidClass);
11388         } else {
11389             this.inputEl().addClass('is-invalid');
11390         }
11391         
11392         
11393         
11394         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11395             
11396             var feedback = this.el.select('.form-control-feedback', true).first();
11397             
11398             if(feedback){
11399                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11400                 
11401                 if(this.getValue().length || this.forceFeedback){
11402                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11403                 }
11404                 
11405             }
11406             
11407         }
11408         
11409         this.fireEvent('invalid', this, msg);
11410     },
11411     // private
11412     SafariOnKeyDown : function(event)
11413     {
11414         // this is a workaround for a password hang bug on chrome/ webkit.
11415         if (this.inputEl().dom.type != 'password') {
11416             return;
11417         }
11418         
11419         var isSelectAll = false;
11420         
11421         if(this.inputEl().dom.selectionEnd > 0){
11422             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11423         }
11424         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11425             event.preventDefault();
11426             this.setValue('');
11427             return;
11428         }
11429         
11430         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11431             
11432             event.preventDefault();
11433             // this is very hacky as keydown always get's upper case.
11434             //
11435             var cc = String.fromCharCode(event.getCharCode());
11436             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11437             
11438         }
11439     },
11440     adjustWidth : function(tag, w){
11441         tag = tag.toLowerCase();
11442         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11443             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11444                 if(tag == 'input'){
11445                     return w + 2;
11446                 }
11447                 if(tag == 'textarea'){
11448                     return w-2;
11449                 }
11450             }else if(Roo.isOpera){
11451                 if(tag == 'input'){
11452                     return w + 2;
11453                 }
11454                 if(tag == 'textarea'){
11455                     return w-2;
11456                 }
11457             }
11458         }
11459         return w;
11460     },
11461     
11462     setFieldLabel : function(v)
11463     {
11464         if(!this.rendered){
11465             return;
11466         }
11467         
11468         if(this.indicatorEl()){
11469             var ar = this.el.select('label > span',true);
11470             
11471             if (ar.elements.length) {
11472                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11473                 this.fieldLabel = v;
11474                 return;
11475             }
11476             
11477             var br = this.el.select('label',true);
11478             
11479             if(br.elements.length) {
11480                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11481                 this.fieldLabel = v;
11482                 return;
11483             }
11484             
11485             Roo.log('Cannot Found any of label > span || label in input');
11486             return;
11487         }
11488         
11489         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11490         this.fieldLabel = v;
11491         
11492         
11493     }
11494 });
11495
11496  
11497 /*
11498  * - LGPL
11499  *
11500  * Input
11501  * 
11502  */
11503
11504 /**
11505  * @class Roo.bootstrap.TextArea
11506  * @extends Roo.bootstrap.Input
11507  * Bootstrap TextArea class
11508  * @cfg {Number} cols Specifies the visible width of a text area
11509  * @cfg {Number} rows Specifies the visible number of lines in a text area
11510  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11511  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11512  * @cfg {string} html text
11513  * 
11514  * @constructor
11515  * Create a new TextArea
11516  * @param {Object} config The config object
11517  */
11518
11519 Roo.bootstrap.TextArea = function(config){
11520     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11521    
11522 };
11523
11524 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11525      
11526     cols : false,
11527     rows : 5,
11528     readOnly : false,
11529     warp : 'soft',
11530     resize : false,
11531     value: false,
11532     html: false,
11533     
11534     getAutoCreate : function(){
11535         
11536         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11537         
11538         var id = Roo.id();
11539         
11540         var cfg = {};
11541         
11542         if(this.inputType != 'hidden'){
11543             cfg.cls = 'form-group' //input-group
11544         }
11545         
11546         var input =  {
11547             tag: 'textarea',
11548             id : id,
11549             warp : this.warp,
11550             rows : this.rows,
11551             value : this.value || '',
11552             html: this.html || '',
11553             cls : 'form-control',
11554             placeholder : this.placeholder || '' 
11555             
11556         };
11557         
11558         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11559             input.maxLength = this.maxLength;
11560         }
11561         
11562         if(this.resize){
11563             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11564         }
11565         
11566         if(this.cols){
11567             input.cols = this.cols;
11568         }
11569         
11570         if (this.readOnly) {
11571             input.readonly = true;
11572         }
11573         
11574         if (this.name) {
11575             input.name = this.name;
11576         }
11577         
11578         if (this.size) {
11579             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11580         }
11581         
11582         var settings=this;
11583         ['xs','sm','md','lg'].map(function(size){
11584             if (settings[size]) {
11585                 cfg.cls += ' col-' + size + '-' + settings[size];
11586             }
11587         });
11588         
11589         var inputblock = input;
11590         
11591         if(this.hasFeedback && !this.allowBlank){
11592             
11593             var feedback = {
11594                 tag: 'span',
11595                 cls: 'glyphicon form-control-feedback'
11596             };
11597
11598             inputblock = {
11599                 cls : 'has-feedback',
11600                 cn :  [
11601                     input,
11602                     feedback
11603                 ] 
11604             };  
11605         }
11606         
11607         
11608         if (this.before || this.after) {
11609             
11610             inputblock = {
11611                 cls : 'input-group',
11612                 cn :  [] 
11613             };
11614             if (this.before) {
11615                 inputblock.cn.push({
11616                     tag :'span',
11617                     cls : 'input-group-addon',
11618                     html : this.before
11619                 });
11620             }
11621             
11622             inputblock.cn.push(input);
11623             
11624             if(this.hasFeedback && !this.allowBlank){
11625                 inputblock.cls += ' has-feedback';
11626                 inputblock.cn.push(feedback);
11627             }
11628             
11629             if (this.after) {
11630                 inputblock.cn.push({
11631                     tag :'span',
11632                     cls : 'input-group-addon',
11633                     html : this.after
11634                 });
11635             }
11636             
11637         }
11638         
11639         if (align ==='left' && this.fieldLabel.length) {
11640             cfg.cn = [
11641                 {
11642                     tag: 'label',
11643                     'for' :  id,
11644                     cls : 'control-label',
11645                     html : this.fieldLabel
11646                 },
11647                 {
11648                     cls : "",
11649                     cn: [
11650                         inputblock
11651                     ]
11652                 }
11653
11654             ];
11655             
11656             if(this.labelWidth > 12){
11657                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11658             }
11659
11660             if(this.labelWidth < 13 && this.labelmd == 0){
11661                 this.labelmd = this.labelWidth;
11662             }
11663
11664             if(this.labellg > 0){
11665                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11666                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11667             }
11668
11669             if(this.labelmd > 0){
11670                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11671                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11672             }
11673
11674             if(this.labelsm > 0){
11675                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11676                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11677             }
11678
11679             if(this.labelxs > 0){
11680                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11681                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11682             }
11683             
11684         } else if ( this.fieldLabel.length) {
11685             cfg.cn = [
11686
11687                {
11688                    tag: 'label',
11689                    //cls : 'input-group-addon',
11690                    html : this.fieldLabel
11691
11692                },
11693
11694                inputblock
11695
11696            ];
11697
11698         } else {
11699
11700             cfg.cn = [
11701
11702                 inputblock
11703
11704             ];
11705                 
11706         }
11707         
11708         if (this.disabled) {
11709             input.disabled=true;
11710         }
11711         
11712         return cfg;
11713         
11714     },
11715     /**
11716      * return the real textarea element.
11717      */
11718     inputEl: function ()
11719     {
11720         return this.el.select('textarea.form-control',true).first();
11721     },
11722     
11723     /**
11724      * Clear any invalid styles/messages for this field
11725      */
11726     clearInvalid : function()
11727     {
11728         
11729         if(!this.el || this.preventMark){ // not rendered
11730             return;
11731         }
11732         
11733         var label = this.el.select('label', true).first();
11734         var icon = this.el.select('i.fa-star', true).first();
11735         
11736         if(label && icon){
11737             icon.remove();
11738         }
11739         this.el.removeClass( this.validClass);
11740         this.inputEl().removeClass('is-invalid');
11741          
11742         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11743             
11744             var feedback = this.el.select('.form-control-feedback', true).first();
11745             
11746             if(feedback){
11747                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11748             }
11749             
11750         }
11751         
11752         this.fireEvent('valid', this);
11753     },
11754     
11755      /**
11756      * Mark this field as valid
11757      */
11758     markValid : function()
11759     {
11760         if(!this.el  || this.preventMark){ // not rendered
11761             return;
11762         }
11763         
11764         this.el.removeClass([this.invalidClass, this.validClass]);
11765         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11766         
11767         var feedback = this.el.select('.form-control-feedback', true).first();
11768             
11769         if(feedback){
11770             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11771         }
11772
11773         if(this.disabled || this.allowBlank){
11774             return;
11775         }
11776         
11777         var label = this.el.select('label', true).first();
11778         var icon = this.el.select('i.fa-star', true).first();
11779         
11780         if(label && icon){
11781             icon.remove();
11782         }
11783         if (Roo.bootstrap.version == 3) {
11784             this.el.addClass(this.validClass);
11785         } else {
11786             this.inputEl().addClass('is-valid');
11787         }
11788         
11789         
11790         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11791             
11792             var feedback = this.el.select('.form-control-feedback', true).first();
11793             
11794             if(feedback){
11795                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11796                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11797             }
11798             
11799         }
11800         
11801         this.fireEvent('valid', this);
11802     },
11803     
11804      /**
11805      * Mark this field as invalid
11806      * @param {String} msg The validation message
11807      */
11808     markInvalid : function(msg)
11809     {
11810         if(!this.el  || this.preventMark){ // not rendered
11811             return;
11812         }
11813         
11814         this.el.removeClass([this.invalidClass, this.validClass]);
11815         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11816         
11817         var feedback = this.el.select('.form-control-feedback', true).first();
11818             
11819         if(feedback){
11820             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11821         }
11822
11823         if(this.disabled || this.allowBlank){
11824             return;
11825         }
11826         
11827         var label = this.el.select('label', true).first();
11828         var icon = this.el.select('i.fa-star', true).first();
11829         
11830         if(!this.getValue().length && label && !icon){
11831             this.el.createChild({
11832                 tag : 'i',
11833                 cls : 'text-danger fa fa-lg fa-star',
11834                 tooltip : 'This field is required',
11835                 style : 'margin-right:5px;'
11836             }, label, true);
11837         }
11838         
11839         if (Roo.bootstrap.version == 3) {
11840             this.el.addClass(this.invalidClass);
11841         } else {
11842             this.inputEl().addClass('is-invalid');
11843         }
11844         
11845         // fixme ... this may be depricated need to test..
11846         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11847             
11848             var feedback = this.el.select('.form-control-feedback', true).first();
11849             
11850             if(feedback){
11851                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11852                 
11853                 if(this.getValue().length || this.forceFeedback){
11854                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11855                 }
11856                 
11857             }
11858             
11859         }
11860         
11861         this.fireEvent('invalid', this, msg);
11862     }
11863 });
11864
11865  
11866 /*
11867  * - LGPL
11868  *
11869  * trigger field - base class for combo..
11870  * 
11871  */
11872  
11873 /**
11874  * @class Roo.bootstrap.TriggerField
11875  * @extends Roo.bootstrap.Input
11876  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11877  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11878  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11879  * for which you can provide a custom implementation.  For example:
11880  * <pre><code>
11881 var trigger = new Roo.bootstrap.TriggerField();
11882 trigger.onTriggerClick = myTriggerFn;
11883 trigger.applyTo('my-field');
11884 </code></pre>
11885  *
11886  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11887  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11888  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11889  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11890  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11891
11892  * @constructor
11893  * Create a new TriggerField.
11894  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11895  * to the base TextField)
11896  */
11897 Roo.bootstrap.TriggerField = function(config){
11898     this.mimicing = false;
11899     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11900 };
11901
11902 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11903     /**
11904      * @cfg {String} triggerClass A CSS class to apply to the trigger
11905      */
11906      /**
11907      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11908      */
11909     hideTrigger:false,
11910
11911     /**
11912      * @cfg {Boolean} removable (true|false) special filter default false
11913      */
11914     removable : false,
11915     
11916     /** @cfg {Boolean} grow @hide */
11917     /** @cfg {Number} growMin @hide */
11918     /** @cfg {Number} growMax @hide */
11919
11920     /**
11921      * @hide 
11922      * @method
11923      */
11924     autoSize: Roo.emptyFn,
11925     // private
11926     monitorTab : true,
11927     // private
11928     deferHeight : true,
11929
11930     
11931     actionMode : 'wrap',
11932     
11933     caret : false,
11934     
11935     
11936     getAutoCreate : function(){
11937        
11938         var align = this.labelAlign || this.parentLabelAlign();
11939         
11940         var id = Roo.id();
11941         
11942         var cfg = {
11943             cls: 'form-group' //input-group
11944         };
11945         
11946         
11947         var input =  {
11948             tag: 'input',
11949             id : id,
11950             type : this.inputType,
11951             cls : 'form-control',
11952             autocomplete: 'new-password',
11953             placeholder : this.placeholder || '' 
11954             
11955         };
11956         if (this.name) {
11957             input.name = this.name;
11958         }
11959         if (this.size) {
11960             input.cls += ' input-' + this.size;
11961         }
11962         
11963         if (this.disabled) {
11964             input.disabled=true;
11965         }
11966         
11967         var inputblock = input;
11968         
11969         if(this.hasFeedback && !this.allowBlank){
11970             
11971             var feedback = {
11972                 tag: 'span',
11973                 cls: 'glyphicon form-control-feedback'
11974             };
11975             
11976             if(this.removable && !this.editable  ){
11977                 inputblock = {
11978                     cls : 'has-feedback',
11979                     cn :  [
11980                         inputblock,
11981                         {
11982                             tag: 'button',
11983                             html : 'x',
11984                             cls : 'roo-combo-removable-btn close'
11985                         },
11986                         feedback
11987                     ] 
11988                 };
11989             } else {
11990                 inputblock = {
11991                     cls : 'has-feedback',
11992                     cn :  [
11993                         inputblock,
11994                         feedback
11995                     ] 
11996                 };
11997             }
11998
11999         } else {
12000             if(this.removable && !this.editable ){
12001                 inputblock = {
12002                     cls : 'roo-removable',
12003                     cn :  [
12004                         inputblock,
12005                         {
12006                             tag: 'button',
12007                             html : 'x',
12008                             cls : 'roo-combo-removable-btn close'
12009                         }
12010                     ] 
12011                 };
12012             }
12013         }
12014         
12015         if (this.before || this.after) {
12016             
12017             inputblock = {
12018                 cls : 'input-group',
12019                 cn :  [] 
12020             };
12021             if (this.before) {
12022                 inputblock.cn.push({
12023                     tag :'span',
12024                     cls : 'input-group-addon input-group-prepend input-group-text',
12025                     html : this.before
12026                 });
12027             }
12028             
12029             inputblock.cn.push(input);
12030             
12031             if(this.hasFeedback && !this.allowBlank){
12032                 inputblock.cls += ' has-feedback';
12033                 inputblock.cn.push(feedback);
12034             }
12035             
12036             if (this.after) {
12037                 inputblock.cn.push({
12038                     tag :'span',
12039                     cls : 'input-group-addon input-group-append input-group-text',
12040                     html : this.after
12041                 });
12042             }
12043             
12044         };
12045         
12046       
12047         
12048         var ibwrap = inputblock;
12049         
12050         if(this.multiple){
12051             ibwrap = {
12052                 tag: 'ul',
12053                 cls: 'roo-select2-choices',
12054                 cn:[
12055                     {
12056                         tag: 'li',
12057                         cls: 'roo-select2-search-field',
12058                         cn: [
12059
12060                             inputblock
12061                         ]
12062                     }
12063                 ]
12064             };
12065                 
12066         }
12067         
12068         var combobox = {
12069             cls: 'roo-select2-container input-group',
12070             cn: [
12071                  {
12072                     tag: 'input',
12073                     type : 'hidden',
12074                     cls: 'form-hidden-field'
12075                 },
12076                 ibwrap
12077             ]
12078         };
12079         
12080         if(!this.multiple && this.showToggleBtn){
12081             
12082             var caret = {
12083                         tag: 'span',
12084                         cls: 'caret'
12085              };
12086             if (this.caret != false) {
12087                 caret = {
12088                      tag: 'i',
12089                      cls: 'fa fa-' + this.caret
12090                 };
12091                 
12092             }
12093             
12094             combobox.cn.push({
12095                 tag :'span',
12096                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12097                 cn : [
12098                     Roo.bootstrap.version == 3 ? caret : '',
12099                     {
12100                         tag: 'span',
12101                         cls: 'combobox-clear',
12102                         cn  : [
12103                             {
12104                                 tag : 'i',
12105                                 cls: 'icon-remove'
12106                             }
12107                         ]
12108                     }
12109                 ]
12110
12111             })
12112         }
12113         
12114         if(this.multiple){
12115             combobox.cls += ' roo-select2-container-multi';
12116         }
12117          var indicator = {
12118             tag : 'i',
12119             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12120             tooltip : 'This field is required'
12121         };
12122         if (Roo.bootstrap.version == 4) {
12123             indicator = {
12124                 tag : 'i',
12125                 style : 'display:none'
12126             };
12127         }
12128         
12129         
12130         if (align ==='left' && this.fieldLabel.length) {
12131             
12132             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12133
12134             cfg.cn = [
12135                 indicator,
12136                 {
12137                     tag: 'label',
12138                     'for' :  id,
12139                     cls : 'control-label',
12140                     html : this.fieldLabel
12141
12142                 },
12143                 {
12144                     cls : "", 
12145                     cn: [
12146                         combobox
12147                     ]
12148                 }
12149
12150             ];
12151             
12152             var labelCfg = cfg.cn[1];
12153             var contentCfg = cfg.cn[2];
12154             
12155             if(this.indicatorpos == 'right'){
12156                 cfg.cn = [
12157                     {
12158                         tag: 'label',
12159                         'for' :  id,
12160                         cls : 'control-label',
12161                         cn : [
12162                             {
12163                                 tag : 'span',
12164                                 html : this.fieldLabel
12165                             },
12166                             indicator
12167                         ]
12168                     },
12169                     {
12170                         cls : "", 
12171                         cn: [
12172                             combobox
12173                         ]
12174                     }
12175
12176                 ];
12177                 
12178                 labelCfg = cfg.cn[0];
12179                 contentCfg = cfg.cn[1];
12180             }
12181             
12182             if(this.labelWidth > 12){
12183                 labelCfg.style = "width: " + this.labelWidth + 'px';
12184             }
12185             
12186             if(this.labelWidth < 13 && this.labelmd == 0){
12187                 this.labelmd = this.labelWidth;
12188             }
12189             
12190             if(this.labellg > 0){
12191                 labelCfg.cls += ' col-lg-' + this.labellg;
12192                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12193             }
12194             
12195             if(this.labelmd > 0){
12196                 labelCfg.cls += ' col-md-' + this.labelmd;
12197                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12198             }
12199             
12200             if(this.labelsm > 0){
12201                 labelCfg.cls += ' col-sm-' + this.labelsm;
12202                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12203             }
12204             
12205             if(this.labelxs > 0){
12206                 labelCfg.cls += ' col-xs-' + this.labelxs;
12207                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12208             }
12209             
12210         } else if ( this.fieldLabel.length) {
12211 //                Roo.log(" label");
12212             cfg.cn = [
12213                 indicator,
12214                {
12215                    tag: 'label',
12216                    //cls : 'input-group-addon',
12217                    html : this.fieldLabel
12218
12219                },
12220
12221                combobox
12222
12223             ];
12224             
12225             if(this.indicatorpos == 'right'){
12226                 
12227                 cfg.cn = [
12228                     {
12229                        tag: 'label',
12230                        cn : [
12231                            {
12232                                tag : 'span',
12233                                html : this.fieldLabel
12234                            },
12235                            indicator
12236                        ]
12237
12238                     },
12239                     combobox
12240
12241                 ];
12242
12243             }
12244
12245         } else {
12246             
12247 //                Roo.log(" no label && no align");
12248                 cfg = combobox
12249                      
12250                 
12251         }
12252         
12253         var settings=this;
12254         ['xs','sm','md','lg'].map(function(size){
12255             if (settings[size]) {
12256                 cfg.cls += ' col-' + size + '-' + settings[size];
12257             }
12258         });
12259         
12260         return cfg;
12261         
12262     },
12263     
12264     
12265     
12266     // private
12267     onResize : function(w, h){
12268 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12269 //        if(typeof w == 'number'){
12270 //            var x = w - this.trigger.getWidth();
12271 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12272 //            this.trigger.setStyle('left', x+'px');
12273 //        }
12274     },
12275
12276     // private
12277     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12278
12279     // private
12280     getResizeEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     getPositionEl : function(){
12286         return this.inputEl();
12287     },
12288
12289     // private
12290     alignErrorIcon : function(){
12291         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12292     },
12293
12294     // private
12295     initEvents : function(){
12296         
12297         this.createList();
12298         
12299         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12300         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12301         if(!this.multiple && this.showToggleBtn){
12302             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12303             if(this.hideTrigger){
12304                 this.trigger.setDisplayed(false);
12305             }
12306             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12307         }
12308         
12309         if(this.multiple){
12310             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12311         }
12312         
12313         if(this.removable && !this.editable && !this.tickable){
12314             var close = this.closeTriggerEl();
12315             
12316             if(close){
12317                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12318                 close.on('click', this.removeBtnClick, this, close);
12319             }
12320         }
12321         
12322         //this.trigger.addClassOnOver('x-form-trigger-over');
12323         //this.trigger.addClassOnClick('x-form-trigger-click');
12324         
12325         //if(!this.width){
12326         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12327         //}
12328     },
12329     
12330     closeTriggerEl : function()
12331     {
12332         var close = this.el.select('.roo-combo-removable-btn', true).first();
12333         return close ? close : false;
12334     },
12335     
12336     removeBtnClick : function(e, h, el)
12337     {
12338         e.preventDefault();
12339         
12340         if(this.fireEvent("remove", this) !== false){
12341             this.reset();
12342             this.fireEvent("afterremove", this)
12343         }
12344     },
12345     
12346     createList : function()
12347     {
12348         this.list = Roo.get(document.body).createChild({
12349             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12350             cls: 'typeahead typeahead-long dropdown-menu shadow',
12351             style: 'display:none'
12352         });
12353         
12354         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12355         
12356     },
12357
12358     // private
12359     initTrigger : function(){
12360        
12361     },
12362
12363     // private
12364     onDestroy : function(){
12365         if(this.trigger){
12366             this.trigger.removeAllListeners();
12367           //  this.trigger.remove();
12368         }
12369         //if(this.wrap){
12370         //    this.wrap.remove();
12371         //}
12372         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12373     },
12374
12375     // private
12376     onFocus : function(){
12377         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12378         /*
12379         if(!this.mimicing){
12380             this.wrap.addClass('x-trigger-wrap-focus');
12381             this.mimicing = true;
12382             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12383             if(this.monitorTab){
12384                 this.el.on("keydown", this.checkTab, this);
12385             }
12386         }
12387         */
12388     },
12389
12390     // private
12391     checkTab : function(e){
12392         if(e.getKey() == e.TAB){
12393             this.triggerBlur();
12394         }
12395     },
12396
12397     // private
12398     onBlur : function(){
12399         // do nothing
12400     },
12401
12402     // private
12403     mimicBlur : function(e, t){
12404         /*
12405         if(!this.wrap.contains(t) && this.validateBlur()){
12406             this.triggerBlur();
12407         }
12408         */
12409     },
12410
12411     // private
12412     triggerBlur : function(){
12413         this.mimicing = false;
12414         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12415         if(this.monitorTab){
12416             this.el.un("keydown", this.checkTab, this);
12417         }
12418         //this.wrap.removeClass('x-trigger-wrap-focus');
12419         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12420     },
12421
12422     // private
12423     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12424     validateBlur : function(e, t){
12425         return true;
12426     },
12427
12428     // private
12429     onDisable : function(){
12430         this.inputEl().dom.disabled = true;
12431         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12432         //if(this.wrap){
12433         //    this.wrap.addClass('x-item-disabled');
12434         //}
12435     },
12436
12437     // private
12438     onEnable : function(){
12439         this.inputEl().dom.disabled = false;
12440         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12441         //if(this.wrap){
12442         //    this.el.removeClass('x-item-disabled');
12443         //}
12444     },
12445
12446     // private
12447     onShow : function(){
12448         var ae = this.getActionEl();
12449         
12450         if(ae){
12451             ae.dom.style.display = '';
12452             ae.dom.style.visibility = 'visible';
12453         }
12454     },
12455
12456     // private
12457     
12458     onHide : function(){
12459         var ae = this.getActionEl();
12460         ae.dom.style.display = 'none';
12461     },
12462
12463     /**
12464      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12465      * by an implementing function.
12466      * @method
12467      * @param {EventObject} e
12468      */
12469     onTriggerClick : Roo.emptyFn
12470 });
12471  
12472 /*
12473 * Licence: LGPL
12474 */
12475
12476 /**
12477  * @class Roo.bootstrap.CardUploader
12478  * @extends Roo.bootstrap.Button
12479  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12480  * @cfg {Number} errorTimeout default 3000
12481  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12482  * @cfg {Array}  html The button text.
12483
12484  *
12485  * @constructor
12486  * Create a new CardUploader
12487  * @param {Object} config The config object
12488  */
12489
12490 Roo.bootstrap.CardUploader = function(config){
12491     
12492  
12493     
12494     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12495     
12496     
12497     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12498         return r.data.id
12499         });
12500     
12501     
12502 };
12503
12504 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12505     
12506      
12507     errorTimeout : 3000,
12508      
12509     images : false,
12510    
12511     fileCollection : false,
12512     allowBlank : true,
12513     
12514     getAutoCreate : function()
12515     {
12516         
12517         var cfg =  {
12518             cls :'form-group' ,
12519             cn : [
12520                
12521                 {
12522                     tag: 'label',
12523                    //cls : 'input-group-addon',
12524                     html : this.fieldLabel
12525
12526                 },
12527
12528                 {
12529                     tag: 'input',
12530                     type : 'hidden',
12531                     nane : this.name,
12532                     value : this.value,
12533                     cls : 'd-none  form-control'
12534                 },
12535                 
12536                 {
12537                     tag: 'input',
12538                     multiple : 'multiple',
12539                     type : 'file',
12540                     cls : 'd-none  roo-card-upload-selector'
12541                 },
12542                 
12543                 {
12544                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12545                 },
12546                 {
12547                     cls : 'card-columns roo-card-uploader-container'
12548                 }
12549
12550             ]
12551         };
12552            
12553          
12554         return cfg;
12555     },
12556     
12557     getChildContainer : function() /// what children are added to.
12558     {
12559         return this.containerEl;
12560     },
12561    
12562     getButtonContainer : function() /// what children are added to.
12563     {
12564         return this.el.select(".roo-card-uploader-button-container").first();
12565     },
12566    
12567     initEvents : function()
12568     {
12569         
12570         Roo.bootstrap.Input.prototype.initEvents.call(this);
12571         
12572         var t = this;
12573         this.addxtype({
12574             xns: Roo.bootstrap,
12575
12576             xtype : 'Button',
12577             container_method : 'getButtonContainer' ,            
12578             html :  this.html, // fix changable?
12579             cls : 'w-100 ',
12580             listeners : {
12581                 'click' : function(btn, e) {
12582                     t.onClick(e);
12583                 }
12584             }
12585         });
12586         
12587         
12588         
12589         
12590         this.urlAPI = (window.createObjectURL && window) || 
12591                                 (window.URL && URL.revokeObjectURL && URL) || 
12592                                 (window.webkitURL && webkitURL);
12593                         
12594          
12595          
12596          
12597         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12598         
12599         this.selectorEl.on('change', this.onFileSelected, this);
12600         if (this.images) {
12601             var t = this;
12602             this.images.forEach(function(img) {
12603                 t.addCard(img)
12604             });
12605             this.images = false;
12606         }
12607         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12608          
12609        
12610     },
12611     
12612    
12613     onClick : function(e)
12614     {
12615         e.preventDefault();
12616          
12617         this.selectorEl.dom.click();
12618          
12619     },
12620     
12621     onFileSelected : function(e)
12622     {
12623         e.preventDefault();
12624         
12625         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12626             return;
12627         }
12628         
12629         Roo.each(this.selectorEl.dom.files, function(file){    
12630             this.addFile(file);
12631         }, this);
12632          
12633     },
12634     
12635       
12636     
12637       
12638     
12639     addFile : function(file)
12640     {
12641            
12642         if(typeof(file) === 'string'){
12643             throw "Add file by name?"; // should not happen
12644             return;
12645         }
12646         
12647         if(!file || !this.urlAPI){
12648             return;
12649         }
12650         
12651         // file;
12652         // file.type;
12653         
12654         var _this = this;
12655         
12656         
12657         var url = _this.urlAPI.createObjectURL( file);
12658            
12659         this.addCard({
12660             id : Roo.bootstrap.CardUploader.ID--,
12661             is_uploaded : false,
12662             src : url,
12663             title : file.name,
12664             mimetype : file.type,
12665             preview : false,
12666             is_deleted : 0
12667         })
12668         
12669     },
12670     
12671     addCard : function (data)
12672     {
12673         // hidden input element?
12674         // if the file is not an image...
12675         //then we need to use something other that and header_image
12676         var t = this;
12677         //   remove.....
12678         var footer = [
12679             {
12680                 xns : Roo.bootstrap,
12681                 xtype : 'CardFooter',
12682                 items: [
12683                     {
12684                         xns : Roo.bootstrap,
12685                         xtype : 'Element',
12686                         cls : 'd-flex',
12687                         items : [
12688                             
12689                             {
12690                                 xns : Roo.bootstrap,
12691                                 xtype : 'Button',
12692                                 html : String.format("<small>{0}</small>", data.title),
12693                                 cls : 'col-11 text-left',
12694                                 size: 'sm',
12695                                 weight: 'link',
12696                                 fa : 'download',
12697                                 listeners : {
12698                                     click : function() {
12699                                         this.downloadCard(data.id)
12700                                     }
12701                                 }
12702                             },
12703                           
12704                             {
12705                                 xns : Roo.bootstrap,
12706                                 xtype : 'Button',
12707                                 
12708                                 size : 'sm',
12709                                 weight: 'danger',
12710                                 cls : 'col-1',
12711                                 fa : 'times',
12712                                 listeners : {
12713                                     click : function() {
12714                                         t.removeCard(data.id)
12715                                     }
12716                                 }
12717                             }
12718                         ]
12719                     }
12720                     
12721                 ] 
12722             }
12723             
12724         ];
12725         
12726         var cn = this.addxtype(
12727             {
12728                  
12729                 xns : Roo.bootstrap,
12730                 xtype : 'Card',
12731                 closeable : true,
12732                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12733                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12734                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12735                 data : data,
12736                 html : false,
12737                  
12738                 items : footer,
12739                 initEvents : function() {
12740                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12741                     this.imgEl = this.el.select('.card-img-top').first();
12742                     if (this.imgEl) {
12743                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12744                         this.imgEl.set({ 'pointer' : 'cursor' });
12745                                   
12746                     }
12747                     
12748                   
12749                 }
12750                 
12751             }
12752         );
12753         // dont' really need ot update items.
12754         // this.items.push(cn);
12755         this.fileCollection.add(cn);
12756         
12757         var _t = this;
12758         var reader = new FileReader();
12759         reader.onloadend = function(evt) {  
12760             data.srcdata =  evt.target.result;
12761             _t.updateInput();
12762         };
12763         reader.readAsDataURL(data.src);
12764         
12765         
12766         
12767     },
12768     removeCard : function(id)
12769     {
12770         
12771         var card  = this.fileCollection.get(id);
12772         card.data.is_deleted = 1;
12773         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12774         this.fileCollection.remove(card);
12775         //this.items = this.items.filter(function(e) { return e != card });
12776         // dont' really need ot update items.
12777         card.el.dom.parentNode.removeChild(card.el.dom);
12778         
12779     },
12780     reset: function()
12781     {
12782         this.fileCollection.each(function(card) {
12783             card.el.dom.parentNode.removeChild(card.el.dom);    
12784         });
12785         this.fileCollection.clear();
12786         this.updateInput();
12787     },
12788     
12789     updateInput : function()
12790     {
12791         var i =0;
12792         var data = [];
12793         var dom = this.inputEl().dom;
12794         var fc = this.fileCollection;
12795         var next = function() {
12796             if (i >= fc.length) {
12797                 dom.value = JSON.stringify(data);
12798                 return;
12799             }
12800             var reader = new FileReader();
12801             reader.onloadend = function(evt) {  
12802                 // file is loaded
12803                 var ee = Roo.apply({}, fc[i]);
12804                 ee.src = evt.target.result;
12805                 data.push(ee);
12806                 i++;
12807                 next();
12808             };
12809             reader.readAsDataURL(fc[i].src);
12810             
12811         }
12812         next();
12813         
12814         
12815     }
12816     
12817     
12818 });
12819
12820
12821 Roo.bootstrap.CardUploader.ID = -1;/*
12822  * Based on:
12823  * Ext JS Library 1.1.1
12824  * Copyright(c) 2006-2007, Ext JS, LLC.
12825  *
12826  * Originally Released Under LGPL - original licence link has changed is not relivant.
12827  *
12828  * Fork - LGPL
12829  * <script type="text/javascript">
12830  */
12831
12832
12833 /**
12834  * @class Roo.data.SortTypes
12835  * @singleton
12836  * Defines the default sorting (casting?) comparison functions used when sorting data.
12837  */
12838 Roo.data.SortTypes = {
12839     /**
12840      * Default sort that does nothing
12841      * @param {Mixed} s The value being converted
12842      * @return {Mixed} The comparison value
12843      */
12844     none : function(s){
12845         return s;
12846     },
12847     
12848     /**
12849      * The regular expression used to strip tags
12850      * @type {RegExp}
12851      * @property
12852      */
12853     stripTagsRE : /<\/?[^>]+>/gi,
12854     
12855     /**
12856      * Strips all HTML tags to sort on text only
12857      * @param {Mixed} s The value being converted
12858      * @return {String} The comparison value
12859      */
12860     asText : function(s){
12861         return String(s).replace(this.stripTagsRE, "");
12862     },
12863     
12864     /**
12865      * Strips all HTML tags to sort on text only - Case insensitive
12866      * @param {Mixed} s The value being converted
12867      * @return {String} The comparison value
12868      */
12869     asUCText : function(s){
12870         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12871     },
12872     
12873     /**
12874      * Case insensitive string
12875      * @param {Mixed} s The value being converted
12876      * @return {String} The comparison value
12877      */
12878     asUCString : function(s) {
12879         return String(s).toUpperCase();
12880     },
12881     
12882     /**
12883      * Date sorting
12884      * @param {Mixed} s The value being converted
12885      * @return {Number} The comparison value
12886      */
12887     asDate : function(s) {
12888         if(!s){
12889             return 0;
12890         }
12891         if(s instanceof Date){
12892             return s.getTime();
12893         }
12894         return Date.parse(String(s));
12895     },
12896     
12897     /**
12898      * Float sorting
12899      * @param {Mixed} s The value being converted
12900      * @return {Float} The comparison value
12901      */
12902     asFloat : function(s) {
12903         var val = parseFloat(String(s).replace(/,/g, ""));
12904         if(isNaN(val)) {
12905             val = 0;
12906         }
12907         return val;
12908     },
12909     
12910     /**
12911      * Integer sorting
12912      * @param {Mixed} s The value being converted
12913      * @return {Number} The comparison value
12914      */
12915     asInt : function(s) {
12916         var val = parseInt(String(s).replace(/,/g, ""));
12917         if(isNaN(val)) {
12918             val = 0;
12919         }
12920         return val;
12921     }
12922 };/*
12923  * Based on:
12924  * Ext JS Library 1.1.1
12925  * Copyright(c) 2006-2007, Ext JS, LLC.
12926  *
12927  * Originally Released Under LGPL - original licence link has changed is not relivant.
12928  *
12929  * Fork - LGPL
12930  * <script type="text/javascript">
12931  */
12932
12933 /**
12934 * @class Roo.data.Record
12935  * Instances of this class encapsulate both record <em>definition</em> information, and record
12936  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12937  * to access Records cached in an {@link Roo.data.Store} object.<br>
12938  * <p>
12939  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12940  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12941  * objects.<br>
12942  * <p>
12943  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12944  * @constructor
12945  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12946  * {@link #create}. The parameters are the same.
12947  * @param {Array} data An associative Array of data values keyed by the field name.
12948  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12949  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12950  * not specified an integer id is generated.
12951  */
12952 Roo.data.Record = function(data, id){
12953     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12954     this.data = data;
12955 };
12956
12957 /**
12958  * Generate a constructor for a specific record layout.
12959  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12960  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12961  * Each field definition object may contain the following properties: <ul>
12962  * <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,
12963  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12964  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12965  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12966  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12967  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12968  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12969  * this may be omitted.</p></li>
12970  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12971  * <ul><li>auto (Default, implies no conversion)</li>
12972  * <li>string</li>
12973  * <li>int</li>
12974  * <li>float</li>
12975  * <li>boolean</li>
12976  * <li>date</li></ul></p></li>
12977  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12978  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12979  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12980  * by the Reader into an object that will be stored in the Record. It is passed the
12981  * following parameters:<ul>
12982  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12983  * </ul></p></li>
12984  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12985  * </ul>
12986  * <br>usage:<br><pre><code>
12987 var TopicRecord = Roo.data.Record.create(
12988     {name: 'title', mapping: 'topic_title'},
12989     {name: 'author', mapping: 'username'},
12990     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12991     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12992     {name: 'lastPoster', mapping: 'user2'},
12993     {name: 'excerpt', mapping: 'post_text'}
12994 );
12995
12996 var myNewRecord = new TopicRecord({
12997     title: 'Do my job please',
12998     author: 'noobie',
12999     totalPosts: 1,
13000     lastPost: new Date(),
13001     lastPoster: 'Animal',
13002     excerpt: 'No way dude!'
13003 });
13004 myStore.add(myNewRecord);
13005 </code></pre>
13006  * @method create
13007  * @static
13008  */
13009 Roo.data.Record.create = function(o){
13010     var f = function(){
13011         f.superclass.constructor.apply(this, arguments);
13012     };
13013     Roo.extend(f, Roo.data.Record);
13014     var p = f.prototype;
13015     p.fields = new Roo.util.MixedCollection(false, function(field){
13016         return field.name;
13017     });
13018     for(var i = 0, len = o.length; i < len; i++){
13019         p.fields.add(new Roo.data.Field(o[i]));
13020     }
13021     f.getField = function(name){
13022         return p.fields.get(name);  
13023     };
13024     return f;
13025 };
13026
13027 Roo.data.Record.AUTO_ID = 1000;
13028 Roo.data.Record.EDIT = 'edit';
13029 Roo.data.Record.REJECT = 'reject';
13030 Roo.data.Record.COMMIT = 'commit';
13031
13032 Roo.data.Record.prototype = {
13033     /**
13034      * Readonly flag - true if this record has been modified.
13035      * @type Boolean
13036      */
13037     dirty : false,
13038     editing : false,
13039     error: null,
13040     modified: null,
13041
13042     // private
13043     join : function(store){
13044         this.store = store;
13045     },
13046
13047     /**
13048      * Set the named field to the specified value.
13049      * @param {String} name The name of the field to set.
13050      * @param {Object} value The value to set the field to.
13051      */
13052     set : function(name, value){
13053         if(this.data[name] == value){
13054             return;
13055         }
13056         this.dirty = true;
13057         if(!this.modified){
13058             this.modified = {};
13059         }
13060         if(typeof this.modified[name] == 'undefined'){
13061             this.modified[name] = this.data[name];
13062         }
13063         this.data[name] = value;
13064         if(!this.editing && this.store){
13065             this.store.afterEdit(this);
13066         }       
13067     },
13068
13069     /**
13070      * Get the value of the named field.
13071      * @param {String} name The name of the field to get the value of.
13072      * @return {Object} The value of the field.
13073      */
13074     get : function(name){
13075         return this.data[name]; 
13076     },
13077
13078     // private
13079     beginEdit : function(){
13080         this.editing = true;
13081         this.modified = {}; 
13082     },
13083
13084     // private
13085     cancelEdit : function(){
13086         this.editing = false;
13087         delete this.modified;
13088     },
13089
13090     // private
13091     endEdit : function(){
13092         this.editing = false;
13093         if(this.dirty && this.store){
13094             this.store.afterEdit(this);
13095         }
13096     },
13097
13098     /**
13099      * Usually called by the {@link Roo.data.Store} which owns the Record.
13100      * Rejects all changes made to the Record since either creation, or the last commit operation.
13101      * Modified fields are reverted to their original values.
13102      * <p>
13103      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13104      * of reject operations.
13105      */
13106     reject : function(){
13107         var m = this.modified;
13108         for(var n in m){
13109             if(typeof m[n] != "function"){
13110                 this.data[n] = m[n];
13111             }
13112         }
13113         this.dirty = false;
13114         delete this.modified;
13115         this.editing = false;
13116         if(this.store){
13117             this.store.afterReject(this);
13118         }
13119     },
13120
13121     /**
13122      * Usually called by the {@link Roo.data.Store} which owns the Record.
13123      * Commits all changes made to the Record since either creation, or the last commit operation.
13124      * <p>
13125      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13126      * of commit operations.
13127      */
13128     commit : function(){
13129         this.dirty = false;
13130         delete this.modified;
13131         this.editing = false;
13132         if(this.store){
13133             this.store.afterCommit(this);
13134         }
13135     },
13136
13137     // private
13138     hasError : function(){
13139         return this.error != null;
13140     },
13141
13142     // private
13143     clearError : function(){
13144         this.error = null;
13145     },
13146
13147     /**
13148      * Creates a copy of this record.
13149      * @param {String} id (optional) A new record id if you don't want to use this record's id
13150      * @return {Record}
13151      */
13152     copy : function(newId) {
13153         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13154     }
13155 };/*
13156  * Based on:
13157  * Ext JS Library 1.1.1
13158  * Copyright(c) 2006-2007, Ext JS, LLC.
13159  *
13160  * Originally Released Under LGPL - original licence link has changed is not relivant.
13161  *
13162  * Fork - LGPL
13163  * <script type="text/javascript">
13164  */
13165
13166
13167
13168 /**
13169  * @class Roo.data.Store
13170  * @extends Roo.util.Observable
13171  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13172  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13173  * <p>
13174  * 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
13175  * has no knowledge of the format of the data returned by the Proxy.<br>
13176  * <p>
13177  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13178  * instances from the data object. These records are cached and made available through accessor functions.
13179  * @constructor
13180  * Creates a new Store.
13181  * @param {Object} config A config object containing the objects needed for the Store to access data,
13182  * and read the data into Records.
13183  */
13184 Roo.data.Store = function(config){
13185     this.data = new Roo.util.MixedCollection(false);
13186     this.data.getKey = function(o){
13187         return o.id;
13188     };
13189     this.baseParams = {};
13190     // private
13191     this.paramNames = {
13192         "start" : "start",
13193         "limit" : "limit",
13194         "sort" : "sort",
13195         "dir" : "dir",
13196         "multisort" : "_multisort"
13197     };
13198
13199     if(config && config.data){
13200         this.inlineData = config.data;
13201         delete config.data;
13202     }
13203
13204     Roo.apply(this, config);
13205     
13206     if(this.reader){ // reader passed
13207         this.reader = Roo.factory(this.reader, Roo.data);
13208         this.reader.xmodule = this.xmodule || false;
13209         if(!this.recordType){
13210             this.recordType = this.reader.recordType;
13211         }
13212         if(this.reader.onMetaChange){
13213             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13214         }
13215     }
13216
13217     if(this.recordType){
13218         this.fields = this.recordType.prototype.fields;
13219     }
13220     this.modified = [];
13221
13222     this.addEvents({
13223         /**
13224          * @event datachanged
13225          * Fires when the data cache has changed, and a widget which is using this Store
13226          * as a Record cache should refresh its view.
13227          * @param {Store} this
13228          */
13229         datachanged : true,
13230         /**
13231          * @event metachange
13232          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13233          * @param {Store} this
13234          * @param {Object} meta The JSON metadata
13235          */
13236         metachange : true,
13237         /**
13238          * @event add
13239          * Fires when Records have been added to the Store
13240          * @param {Store} this
13241          * @param {Roo.data.Record[]} records The array of Records added
13242          * @param {Number} index The index at which the record(s) were added
13243          */
13244         add : true,
13245         /**
13246          * @event remove
13247          * Fires when a Record has been removed from the Store
13248          * @param {Store} this
13249          * @param {Roo.data.Record} record The Record that was removed
13250          * @param {Number} index The index at which the record was removed
13251          */
13252         remove : true,
13253         /**
13254          * @event update
13255          * Fires when a Record has been updated
13256          * @param {Store} this
13257          * @param {Roo.data.Record} record The Record that was updated
13258          * @param {String} operation The update operation being performed.  Value may be one of:
13259          * <pre><code>
13260  Roo.data.Record.EDIT
13261  Roo.data.Record.REJECT
13262  Roo.data.Record.COMMIT
13263          * </code></pre>
13264          */
13265         update : true,
13266         /**
13267          * @event clear
13268          * Fires when the data cache has been cleared.
13269          * @param {Store} this
13270          */
13271         clear : true,
13272         /**
13273          * @event beforeload
13274          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13275          * the load action will be canceled.
13276          * @param {Store} this
13277          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13278          */
13279         beforeload : true,
13280         /**
13281          * @event beforeloadadd
13282          * Fires after a new set of Records has been loaded.
13283          * @param {Store} this
13284          * @param {Roo.data.Record[]} records The Records that were loaded
13285          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13286          */
13287         beforeloadadd : true,
13288         /**
13289          * @event load
13290          * Fires after a new set of Records has been loaded, before they are added to the store.
13291          * @param {Store} this
13292          * @param {Roo.data.Record[]} records The Records that were loaded
13293          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13294          * @params {Object} return from reader
13295          */
13296         load : true,
13297         /**
13298          * @event loadexception
13299          * Fires if an exception occurs in the Proxy during loading.
13300          * Called with the signature of the Proxy's "loadexception" event.
13301          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13302          * 
13303          * @param {Proxy} 
13304          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13305          * @param {Object} load options 
13306          * @param {Object} jsonData from your request (normally this contains the Exception)
13307          */
13308         loadexception : true
13309     });
13310     
13311     if(this.proxy){
13312         this.proxy = Roo.factory(this.proxy, Roo.data);
13313         this.proxy.xmodule = this.xmodule || false;
13314         this.relayEvents(this.proxy,  ["loadexception"]);
13315     }
13316     this.sortToggle = {};
13317     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13318
13319     Roo.data.Store.superclass.constructor.call(this);
13320
13321     if(this.inlineData){
13322         this.loadData(this.inlineData);
13323         delete this.inlineData;
13324     }
13325 };
13326
13327 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13328      /**
13329     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13330     * without a remote query - used by combo/forms at present.
13331     */
13332     
13333     /**
13334     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13335     */
13336     /**
13337     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13338     */
13339     /**
13340     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13341     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13342     */
13343     /**
13344     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13345     * on any HTTP request
13346     */
13347     /**
13348     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13349     */
13350     /**
13351     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13352     */
13353     multiSort: false,
13354     /**
13355     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13356     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13357     */
13358     remoteSort : false,
13359
13360     /**
13361     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13362      * loaded or when a record is removed. (defaults to false).
13363     */
13364     pruneModifiedRecords : false,
13365
13366     // private
13367     lastOptions : null,
13368
13369     /**
13370      * Add Records to the Store and fires the add event.
13371      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13372      */
13373     add : function(records){
13374         records = [].concat(records);
13375         for(var i = 0, len = records.length; i < len; i++){
13376             records[i].join(this);
13377         }
13378         var index = this.data.length;
13379         this.data.addAll(records);
13380         this.fireEvent("add", this, records, index);
13381     },
13382
13383     /**
13384      * Remove a Record from the Store and fires the remove event.
13385      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13386      */
13387     remove : function(record){
13388         var index = this.data.indexOf(record);
13389         this.data.removeAt(index);
13390  
13391         if(this.pruneModifiedRecords){
13392             this.modified.remove(record);
13393         }
13394         this.fireEvent("remove", this, record, index);
13395     },
13396
13397     /**
13398      * Remove all Records from the Store and fires the clear event.
13399      */
13400     removeAll : function(){
13401         this.data.clear();
13402         if(this.pruneModifiedRecords){
13403             this.modified = [];
13404         }
13405         this.fireEvent("clear", this);
13406     },
13407
13408     /**
13409      * Inserts Records to the Store at the given index and fires the add event.
13410      * @param {Number} index The start index at which to insert the passed Records.
13411      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13412      */
13413     insert : function(index, records){
13414         records = [].concat(records);
13415         for(var i = 0, len = records.length; i < len; i++){
13416             this.data.insert(index, records[i]);
13417             records[i].join(this);
13418         }
13419         this.fireEvent("add", this, records, index);
13420     },
13421
13422     /**
13423      * Get the index within the cache of the passed Record.
13424      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13425      * @return {Number} The index of the passed Record. Returns -1 if not found.
13426      */
13427     indexOf : function(record){
13428         return this.data.indexOf(record);
13429     },
13430
13431     /**
13432      * Get the index within the cache of the Record with the passed id.
13433      * @param {String} id The id of the Record to find.
13434      * @return {Number} The index of the Record. Returns -1 if not found.
13435      */
13436     indexOfId : function(id){
13437         return this.data.indexOfKey(id);
13438     },
13439
13440     /**
13441      * Get the Record with the specified id.
13442      * @param {String} id The id of the Record to find.
13443      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13444      */
13445     getById : function(id){
13446         return this.data.key(id);
13447     },
13448
13449     /**
13450      * Get the Record at the specified index.
13451      * @param {Number} index The index of the Record to find.
13452      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13453      */
13454     getAt : function(index){
13455         return this.data.itemAt(index);
13456     },
13457
13458     /**
13459      * Returns a range of Records between specified indices.
13460      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13461      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13462      * @return {Roo.data.Record[]} An array of Records
13463      */
13464     getRange : function(start, end){
13465         return this.data.getRange(start, end);
13466     },
13467
13468     // private
13469     storeOptions : function(o){
13470         o = Roo.apply({}, o);
13471         delete o.callback;
13472         delete o.scope;
13473         this.lastOptions = o;
13474     },
13475
13476     /**
13477      * Loads the Record cache from the configured Proxy using the configured Reader.
13478      * <p>
13479      * If using remote paging, then the first load call must specify the <em>start</em>
13480      * and <em>limit</em> properties in the options.params property to establish the initial
13481      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13482      * <p>
13483      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13484      * and this call will return before the new data has been loaded. Perform any post-processing
13485      * in a callback function, or in a "load" event handler.</strong>
13486      * <p>
13487      * @param {Object} options An object containing properties which control loading options:<ul>
13488      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13489      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13490      * passed the following arguments:<ul>
13491      * <li>r : Roo.data.Record[]</li>
13492      * <li>options: Options object from the load call</li>
13493      * <li>success: Boolean success indicator</li></ul></li>
13494      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13495      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13496      * </ul>
13497      */
13498     load : function(options){
13499         options = options || {};
13500         if(this.fireEvent("beforeload", this, options) !== false){
13501             this.storeOptions(options);
13502             var p = Roo.apply(options.params || {}, this.baseParams);
13503             // if meta was not loaded from remote source.. try requesting it.
13504             if (!this.reader.metaFromRemote) {
13505                 p._requestMeta = 1;
13506             }
13507             if(this.sortInfo && this.remoteSort){
13508                 var pn = this.paramNames;
13509                 p[pn["sort"]] = this.sortInfo.field;
13510                 p[pn["dir"]] = this.sortInfo.direction;
13511             }
13512             if (this.multiSort) {
13513                 var pn = this.paramNames;
13514                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13515             }
13516             
13517             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13518         }
13519     },
13520
13521     /**
13522      * Reloads the Record cache from the configured Proxy using the configured Reader and
13523      * the options from the last load operation performed.
13524      * @param {Object} options (optional) An object containing properties which may override the options
13525      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13526      * the most recently used options are reused).
13527      */
13528     reload : function(options){
13529         this.load(Roo.applyIf(options||{}, this.lastOptions));
13530     },
13531
13532     // private
13533     // Called as a callback by the Reader during a load operation.
13534     loadRecords : function(o, options, success){
13535         if(!o || success === false){
13536             if(success !== false){
13537                 this.fireEvent("load", this, [], options, o);
13538             }
13539             if(options.callback){
13540                 options.callback.call(options.scope || this, [], options, false);
13541             }
13542             return;
13543         }
13544         // if data returned failure - throw an exception.
13545         if (o.success === false) {
13546             // show a message if no listener is registered.
13547             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13548                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13549             }
13550             // loadmask wil be hooked into this..
13551             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13552             return;
13553         }
13554         var r = o.records, t = o.totalRecords || r.length;
13555         
13556         this.fireEvent("beforeloadadd", this, r, options, o);
13557         
13558         if(!options || options.add !== true){
13559             if(this.pruneModifiedRecords){
13560                 this.modified = [];
13561             }
13562             for(var i = 0, len = r.length; i < len; i++){
13563                 r[i].join(this);
13564             }
13565             if(this.snapshot){
13566                 this.data = this.snapshot;
13567                 delete this.snapshot;
13568             }
13569             this.data.clear();
13570             this.data.addAll(r);
13571             this.totalLength = t;
13572             this.applySort();
13573             this.fireEvent("datachanged", this);
13574         }else{
13575             this.totalLength = Math.max(t, this.data.length+r.length);
13576             this.add(r);
13577         }
13578         
13579         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13580                 
13581             var e = new Roo.data.Record({});
13582
13583             e.set(this.parent.displayField, this.parent.emptyTitle);
13584             e.set(this.parent.valueField, '');
13585
13586             this.insert(0, e);
13587         }
13588             
13589         this.fireEvent("load", this, r, options, o);
13590         if(options.callback){
13591             options.callback.call(options.scope || this, r, options, true);
13592         }
13593     },
13594
13595
13596     /**
13597      * Loads data from a passed data block. A Reader which understands the format of the data
13598      * must have been configured in the constructor.
13599      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13600      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13601      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13602      */
13603     loadData : function(o, append){
13604         var r = this.reader.readRecords(o);
13605         this.loadRecords(r, {add: append}, true);
13606     },
13607     
13608      /**
13609      * using 'cn' the nested child reader read the child array into it's child stores.
13610      * @param {Object} rec The record with a 'children array
13611      */
13612     loadDataFromChildren : function(rec)
13613     {
13614         this.loadData(this.reader.toLoadData(rec));
13615     },
13616     
13617
13618     /**
13619      * Gets the number of cached records.
13620      * <p>
13621      * <em>If using paging, this may not be the total size of the dataset. If the data object
13622      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13623      * the data set size</em>
13624      */
13625     getCount : function(){
13626         return this.data.length || 0;
13627     },
13628
13629     /**
13630      * Gets the total number of records in the dataset as returned by the server.
13631      * <p>
13632      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13633      * the dataset size</em>
13634      */
13635     getTotalCount : function(){
13636         return this.totalLength || 0;
13637     },
13638
13639     /**
13640      * Returns the sort state of the Store as an object with two properties:
13641      * <pre><code>
13642  field {String} The name of the field by which the Records are sorted
13643  direction {String} The sort order, "ASC" or "DESC"
13644      * </code></pre>
13645      */
13646     getSortState : function(){
13647         return this.sortInfo;
13648     },
13649
13650     // private
13651     applySort : function(){
13652         if(this.sortInfo && !this.remoteSort){
13653             var s = this.sortInfo, f = s.field;
13654             var st = this.fields.get(f).sortType;
13655             var fn = function(r1, r2){
13656                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13657                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13658             };
13659             this.data.sort(s.direction, fn);
13660             if(this.snapshot && this.snapshot != this.data){
13661                 this.snapshot.sort(s.direction, fn);
13662             }
13663         }
13664     },
13665
13666     /**
13667      * Sets the default sort column and order to be used by the next load operation.
13668      * @param {String} fieldName The name of the field to sort by.
13669      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13670      */
13671     setDefaultSort : function(field, dir){
13672         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13673     },
13674
13675     /**
13676      * Sort the Records.
13677      * If remote sorting is used, the sort is performed on the server, and the cache is
13678      * reloaded. If local sorting is used, the cache is sorted internally.
13679      * @param {String} fieldName The name of the field to sort by.
13680      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13681      */
13682     sort : function(fieldName, dir){
13683         var f = this.fields.get(fieldName);
13684         if(!dir){
13685             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13686             
13687             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13688                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13689             }else{
13690                 dir = f.sortDir;
13691             }
13692         }
13693         this.sortToggle[f.name] = dir;
13694         this.sortInfo = {field: f.name, direction: dir};
13695         if(!this.remoteSort){
13696             this.applySort();
13697             this.fireEvent("datachanged", this);
13698         }else{
13699             this.load(this.lastOptions);
13700         }
13701     },
13702
13703     /**
13704      * Calls the specified function for each of the Records in the cache.
13705      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13706      * Returning <em>false</em> aborts and exits the iteration.
13707      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13708      */
13709     each : function(fn, scope){
13710         this.data.each(fn, scope);
13711     },
13712
13713     /**
13714      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13715      * (e.g., during paging).
13716      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13717      */
13718     getModifiedRecords : function(){
13719         return this.modified;
13720     },
13721
13722     // private
13723     createFilterFn : function(property, value, anyMatch){
13724         if(!value.exec){ // not a regex
13725             value = String(value);
13726             if(value.length == 0){
13727                 return false;
13728             }
13729             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13730         }
13731         return function(r){
13732             return value.test(r.data[property]);
13733         };
13734     },
13735
13736     /**
13737      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13738      * @param {String} property A field on your records
13739      * @param {Number} start The record index to start at (defaults to 0)
13740      * @param {Number} end The last record index to include (defaults to length - 1)
13741      * @return {Number} The sum
13742      */
13743     sum : function(property, start, end){
13744         var rs = this.data.items, v = 0;
13745         start = start || 0;
13746         end = (end || end === 0) ? end : rs.length-1;
13747
13748         for(var i = start; i <= end; i++){
13749             v += (rs[i].data[property] || 0);
13750         }
13751         return v;
13752     },
13753
13754     /**
13755      * Filter the records by a specified property.
13756      * @param {String} field A field on your records
13757      * @param {String/RegExp} value Either a string that the field
13758      * should start with or a RegExp to test against the field
13759      * @param {Boolean} anyMatch True to match any part not just the beginning
13760      */
13761     filter : function(property, value, anyMatch){
13762         var fn = this.createFilterFn(property, value, anyMatch);
13763         return fn ? this.filterBy(fn) : this.clearFilter();
13764     },
13765
13766     /**
13767      * Filter by a function. The specified function will be called with each
13768      * record in this data source. If the function returns true the record is included,
13769      * otherwise it is filtered.
13770      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13771      * @param {Object} scope (optional) The scope of the function (defaults to this)
13772      */
13773     filterBy : function(fn, scope){
13774         this.snapshot = this.snapshot || this.data;
13775         this.data = this.queryBy(fn, scope||this);
13776         this.fireEvent("datachanged", this);
13777     },
13778
13779     /**
13780      * Query the records by a specified property.
13781      * @param {String} field A field on your records
13782      * @param {String/RegExp} value Either a string that the field
13783      * should start with or a RegExp to test against the field
13784      * @param {Boolean} anyMatch True to match any part not just the beginning
13785      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13786      */
13787     query : function(property, value, anyMatch){
13788         var fn = this.createFilterFn(property, value, anyMatch);
13789         return fn ? this.queryBy(fn) : this.data.clone();
13790     },
13791
13792     /**
13793      * Query by a function. The specified function will be called with each
13794      * record in this data source. If the function returns true the record is included
13795      * in the results.
13796      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13797      * @param {Object} scope (optional) The scope of the function (defaults to this)
13798       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13799      **/
13800     queryBy : function(fn, scope){
13801         var data = this.snapshot || this.data;
13802         return data.filterBy(fn, scope||this);
13803     },
13804
13805     /**
13806      * Collects unique values for a particular dataIndex from this store.
13807      * @param {String} dataIndex The property to collect
13808      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13809      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13810      * @return {Array} An array of the unique values
13811      **/
13812     collect : function(dataIndex, allowNull, bypassFilter){
13813         var d = (bypassFilter === true && this.snapshot) ?
13814                 this.snapshot.items : this.data.items;
13815         var v, sv, r = [], l = {};
13816         for(var i = 0, len = d.length; i < len; i++){
13817             v = d[i].data[dataIndex];
13818             sv = String(v);
13819             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13820                 l[sv] = true;
13821                 r[r.length] = v;
13822             }
13823         }
13824         return r;
13825     },
13826
13827     /**
13828      * Revert to a view of the Record cache with no filtering applied.
13829      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13830      */
13831     clearFilter : function(suppressEvent){
13832         if(this.snapshot && this.snapshot != this.data){
13833             this.data = this.snapshot;
13834             delete this.snapshot;
13835             if(suppressEvent !== true){
13836                 this.fireEvent("datachanged", this);
13837             }
13838         }
13839     },
13840
13841     // private
13842     afterEdit : function(record){
13843         if(this.modified.indexOf(record) == -1){
13844             this.modified.push(record);
13845         }
13846         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13847     },
13848     
13849     // private
13850     afterReject : function(record){
13851         this.modified.remove(record);
13852         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13853     },
13854
13855     // private
13856     afterCommit : function(record){
13857         this.modified.remove(record);
13858         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13859     },
13860
13861     /**
13862      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13863      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13864      */
13865     commitChanges : function(){
13866         var m = this.modified.slice(0);
13867         this.modified = [];
13868         for(var i = 0, len = m.length; i < len; i++){
13869             m[i].commit();
13870         }
13871     },
13872
13873     /**
13874      * Cancel outstanding changes on all changed records.
13875      */
13876     rejectChanges : function(){
13877         var m = this.modified.slice(0);
13878         this.modified = [];
13879         for(var i = 0, len = m.length; i < len; i++){
13880             m[i].reject();
13881         }
13882     },
13883
13884     onMetaChange : function(meta, rtype, o){
13885         this.recordType = rtype;
13886         this.fields = rtype.prototype.fields;
13887         delete this.snapshot;
13888         this.sortInfo = meta.sortInfo || this.sortInfo;
13889         this.modified = [];
13890         this.fireEvent('metachange', this, this.reader.meta);
13891     },
13892     
13893     moveIndex : function(data, type)
13894     {
13895         var index = this.indexOf(data);
13896         
13897         var newIndex = index + type;
13898         
13899         this.remove(data);
13900         
13901         this.insert(newIndex, data);
13902         
13903     }
13904 });/*
13905  * Based on:
13906  * Ext JS Library 1.1.1
13907  * Copyright(c) 2006-2007, Ext JS, LLC.
13908  *
13909  * Originally Released Under LGPL - original licence link has changed is not relivant.
13910  *
13911  * Fork - LGPL
13912  * <script type="text/javascript">
13913  */
13914
13915 /**
13916  * @class Roo.data.SimpleStore
13917  * @extends Roo.data.Store
13918  * Small helper class to make creating Stores from Array data easier.
13919  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13920  * @cfg {Array} fields An array of field definition objects, or field name strings.
13921  * @cfg {Object} an existing reader (eg. copied from another store)
13922  * @cfg {Array} data The multi-dimensional array of data
13923  * @constructor
13924  * @param {Object} config
13925  */
13926 Roo.data.SimpleStore = function(config)
13927 {
13928     Roo.data.SimpleStore.superclass.constructor.call(this, {
13929         isLocal : true,
13930         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13931                 id: config.id
13932             },
13933             Roo.data.Record.create(config.fields)
13934         ),
13935         proxy : new Roo.data.MemoryProxy(config.data)
13936     });
13937     this.load();
13938 };
13939 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13940  * Based on:
13941  * Ext JS Library 1.1.1
13942  * Copyright(c) 2006-2007, Ext JS, LLC.
13943  *
13944  * Originally Released Under LGPL - original licence link has changed is not relivant.
13945  *
13946  * Fork - LGPL
13947  * <script type="text/javascript">
13948  */
13949
13950 /**
13951 /**
13952  * @extends Roo.data.Store
13953  * @class Roo.data.JsonStore
13954  * Small helper class to make creating Stores for JSON data easier. <br/>
13955 <pre><code>
13956 var store = new Roo.data.JsonStore({
13957     url: 'get-images.php',
13958     root: 'images',
13959     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13960 });
13961 </code></pre>
13962  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13963  * JsonReader and HttpProxy (unless inline data is provided).</b>
13964  * @cfg {Array} fields An array of field definition objects, or field name strings.
13965  * @constructor
13966  * @param {Object} config
13967  */
13968 Roo.data.JsonStore = function(c){
13969     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13970         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13971         reader: new Roo.data.JsonReader(c, c.fields)
13972     }));
13973 };
13974 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13975  * Based on:
13976  * Ext JS Library 1.1.1
13977  * Copyright(c) 2006-2007, Ext JS, LLC.
13978  *
13979  * Originally Released Under LGPL - original licence link has changed is not relivant.
13980  *
13981  * Fork - LGPL
13982  * <script type="text/javascript">
13983  */
13984
13985  
13986 Roo.data.Field = function(config){
13987     if(typeof config == "string"){
13988         config = {name: config};
13989     }
13990     Roo.apply(this, config);
13991     
13992     if(!this.type){
13993         this.type = "auto";
13994     }
13995     
13996     var st = Roo.data.SortTypes;
13997     // named sortTypes are supported, here we look them up
13998     if(typeof this.sortType == "string"){
13999         this.sortType = st[this.sortType];
14000     }
14001     
14002     // set default sortType for strings and dates
14003     if(!this.sortType){
14004         switch(this.type){
14005             case "string":
14006                 this.sortType = st.asUCString;
14007                 break;
14008             case "date":
14009                 this.sortType = st.asDate;
14010                 break;
14011             default:
14012                 this.sortType = st.none;
14013         }
14014     }
14015
14016     // define once
14017     var stripRe = /[\$,%]/g;
14018
14019     // prebuilt conversion function for this field, instead of
14020     // switching every time we're reading a value
14021     if(!this.convert){
14022         var cv, dateFormat = this.dateFormat;
14023         switch(this.type){
14024             case "":
14025             case "auto":
14026             case undefined:
14027                 cv = function(v){ return v; };
14028                 break;
14029             case "string":
14030                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14031                 break;
14032             case "int":
14033                 cv = function(v){
14034                     return v !== undefined && v !== null && v !== '' ?
14035                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14036                     };
14037                 break;
14038             case "float":
14039                 cv = function(v){
14040                     return v !== undefined && v !== null && v !== '' ?
14041                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14042                     };
14043                 break;
14044             case "bool":
14045             case "boolean":
14046                 cv = function(v){ return v === true || v === "true" || v == 1; };
14047                 break;
14048             case "date":
14049                 cv = function(v){
14050                     if(!v){
14051                         return '';
14052                     }
14053                     if(v instanceof Date){
14054                         return v;
14055                     }
14056                     if(dateFormat){
14057                         if(dateFormat == "timestamp"){
14058                             return new Date(v*1000);
14059                         }
14060                         return Date.parseDate(v, dateFormat);
14061                     }
14062                     var parsed = Date.parse(v);
14063                     return parsed ? new Date(parsed) : null;
14064                 };
14065              break;
14066             
14067         }
14068         this.convert = cv;
14069     }
14070 };
14071
14072 Roo.data.Field.prototype = {
14073     dateFormat: null,
14074     defaultValue: "",
14075     mapping: null,
14076     sortType : null,
14077     sortDir : "ASC"
14078 };/*
14079  * Based on:
14080  * Ext JS Library 1.1.1
14081  * Copyright(c) 2006-2007, Ext JS, LLC.
14082  *
14083  * Originally Released Under LGPL - original licence link has changed is not relivant.
14084  *
14085  * Fork - LGPL
14086  * <script type="text/javascript">
14087  */
14088  
14089 // Base class for reading structured data from a data source.  This class is intended to be
14090 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14091
14092 /**
14093  * @class Roo.data.DataReader
14094  * Base class for reading structured data from a data source.  This class is intended to be
14095  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14096  */
14097
14098 Roo.data.DataReader = function(meta, recordType){
14099     
14100     this.meta = meta;
14101     
14102     this.recordType = recordType instanceof Array ? 
14103         Roo.data.Record.create(recordType) : recordType;
14104 };
14105
14106 Roo.data.DataReader.prototype = {
14107     
14108     
14109     readerType : 'Data',
14110      /**
14111      * Create an empty record
14112      * @param {Object} data (optional) - overlay some values
14113      * @return {Roo.data.Record} record created.
14114      */
14115     newRow :  function(d) {
14116         var da =  {};
14117         this.recordType.prototype.fields.each(function(c) {
14118             switch( c.type) {
14119                 case 'int' : da[c.name] = 0; break;
14120                 case 'date' : da[c.name] = new Date(); break;
14121                 case 'float' : da[c.name] = 0.0; break;
14122                 case 'boolean' : da[c.name] = false; break;
14123                 default : da[c.name] = ""; break;
14124             }
14125             
14126         });
14127         return new this.recordType(Roo.apply(da, d));
14128     }
14129     
14130     
14131 };/*
14132  * Based on:
14133  * Ext JS Library 1.1.1
14134  * Copyright(c) 2006-2007, Ext JS, LLC.
14135  *
14136  * Originally Released Under LGPL - original licence link has changed is not relivant.
14137  *
14138  * Fork - LGPL
14139  * <script type="text/javascript">
14140  */
14141
14142 /**
14143  * @class Roo.data.DataProxy
14144  * @extends Roo.data.Observable
14145  * This class is an abstract base class for implementations which provide retrieval of
14146  * unformatted data objects.<br>
14147  * <p>
14148  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14149  * (of the appropriate type which knows how to parse the data object) to provide a block of
14150  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14151  * <p>
14152  * Custom implementations must implement the load method as described in
14153  * {@link Roo.data.HttpProxy#load}.
14154  */
14155 Roo.data.DataProxy = function(){
14156     this.addEvents({
14157         /**
14158          * @event beforeload
14159          * Fires before a network request is made to retrieve a data object.
14160          * @param {Object} This DataProxy object.
14161          * @param {Object} params The params parameter to the load function.
14162          */
14163         beforeload : true,
14164         /**
14165          * @event load
14166          * Fires before the load method's callback is called.
14167          * @param {Object} This DataProxy object.
14168          * @param {Object} o The data object.
14169          * @param {Object} arg The callback argument object passed to the load function.
14170          */
14171         load : true,
14172         /**
14173          * @event loadexception
14174          * Fires if an Exception occurs during data retrieval.
14175          * @param {Object} This DataProxy object.
14176          * @param {Object} o The data object.
14177          * @param {Object} arg The callback argument object passed to the load function.
14178          * @param {Object} e The Exception.
14179          */
14180         loadexception : true
14181     });
14182     Roo.data.DataProxy.superclass.constructor.call(this);
14183 };
14184
14185 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14186
14187     /**
14188      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14189      */
14190 /*
14191  * Based on:
14192  * Ext JS Library 1.1.1
14193  * Copyright(c) 2006-2007, Ext JS, LLC.
14194  *
14195  * Originally Released Under LGPL - original licence link has changed is not relivant.
14196  *
14197  * Fork - LGPL
14198  * <script type="text/javascript">
14199  */
14200 /**
14201  * @class Roo.data.MemoryProxy
14202  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14203  * to the Reader when its load method is called.
14204  * @constructor
14205  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14206  */
14207 Roo.data.MemoryProxy = function(data){
14208     if (data.data) {
14209         data = data.data;
14210     }
14211     Roo.data.MemoryProxy.superclass.constructor.call(this);
14212     this.data = data;
14213 };
14214
14215 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14216     
14217     /**
14218      * Load data from the requested source (in this case an in-memory
14219      * data object passed to the constructor), read the data object into
14220      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14221      * process that block using the passed callback.
14222      * @param {Object} params This parameter is not used by the MemoryProxy class.
14223      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14224      * object into a block of Roo.data.Records.
14225      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14226      * The function must be passed <ul>
14227      * <li>The Record block object</li>
14228      * <li>The "arg" argument from the load function</li>
14229      * <li>A boolean success indicator</li>
14230      * </ul>
14231      * @param {Object} scope The scope in which to call the callback
14232      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14233      */
14234     load : function(params, reader, callback, scope, arg){
14235         params = params || {};
14236         var result;
14237         try {
14238             result = reader.readRecords(params.data ? params.data :this.data);
14239         }catch(e){
14240             this.fireEvent("loadexception", this, arg, null, e);
14241             callback.call(scope, null, arg, false);
14242             return;
14243         }
14244         callback.call(scope, result, arg, true);
14245     },
14246     
14247     // private
14248     update : function(params, records){
14249         
14250     }
14251 });/*
14252  * Based on:
14253  * Ext JS Library 1.1.1
14254  * Copyright(c) 2006-2007, Ext JS, LLC.
14255  *
14256  * Originally Released Under LGPL - original licence link has changed is not relivant.
14257  *
14258  * Fork - LGPL
14259  * <script type="text/javascript">
14260  */
14261 /**
14262  * @class Roo.data.HttpProxy
14263  * @extends Roo.data.DataProxy
14264  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14265  * configured to reference a certain URL.<br><br>
14266  * <p>
14267  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14268  * from which the running page was served.<br><br>
14269  * <p>
14270  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14271  * <p>
14272  * Be aware that to enable the browser to parse an XML document, the server must set
14273  * the Content-Type header in the HTTP response to "text/xml".
14274  * @constructor
14275  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14276  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14277  * will be used to make the request.
14278  */
14279 Roo.data.HttpProxy = function(conn){
14280     Roo.data.HttpProxy.superclass.constructor.call(this);
14281     // is conn a conn config or a real conn?
14282     this.conn = conn;
14283     this.useAjax = !conn || !conn.events;
14284   
14285 };
14286
14287 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14288     // thse are take from connection...
14289     
14290     /**
14291      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14292      */
14293     /**
14294      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14295      * extra parameters to each request made by this object. (defaults to undefined)
14296      */
14297     /**
14298      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14299      *  to each request made by this object. (defaults to undefined)
14300      */
14301     /**
14302      * @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)
14303      */
14304     /**
14305      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14306      */
14307      /**
14308      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14309      * @type Boolean
14310      */
14311   
14312
14313     /**
14314      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14315      * @type Boolean
14316      */
14317     /**
14318      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14319      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14320      * a finer-grained basis than the DataProxy events.
14321      */
14322     getConnection : function(){
14323         return this.useAjax ? Roo.Ajax : this.conn;
14324     },
14325
14326     /**
14327      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14328      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14329      * process that block using the passed callback.
14330      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14331      * for the request to the remote server.
14332      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14333      * object into a block of Roo.data.Records.
14334      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14335      * The function must be passed <ul>
14336      * <li>The Record block object</li>
14337      * <li>The "arg" argument from the load function</li>
14338      * <li>A boolean success indicator</li>
14339      * </ul>
14340      * @param {Object} scope The scope in which to call the callback
14341      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14342      */
14343     load : function(params, reader, callback, scope, arg){
14344         if(this.fireEvent("beforeload", this, params) !== false){
14345             var  o = {
14346                 params : params || {},
14347                 request: {
14348                     callback : callback,
14349                     scope : scope,
14350                     arg : arg
14351                 },
14352                 reader: reader,
14353                 callback : this.loadResponse,
14354                 scope: this
14355             };
14356             if(this.useAjax){
14357                 Roo.applyIf(o, this.conn);
14358                 if(this.activeRequest){
14359                     Roo.Ajax.abort(this.activeRequest);
14360                 }
14361                 this.activeRequest = Roo.Ajax.request(o);
14362             }else{
14363                 this.conn.request(o);
14364             }
14365         }else{
14366             callback.call(scope||this, null, arg, false);
14367         }
14368     },
14369
14370     // private
14371     loadResponse : function(o, success, response){
14372         delete this.activeRequest;
14373         if(!success){
14374             this.fireEvent("loadexception", this, o, response);
14375             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14376             return;
14377         }
14378         var result;
14379         try {
14380             result = o.reader.read(response);
14381         }catch(e){
14382             this.fireEvent("loadexception", this, o, response, e);
14383             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14384             return;
14385         }
14386         
14387         this.fireEvent("load", this, o, o.request.arg);
14388         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14389     },
14390
14391     // private
14392     update : function(dataSet){
14393
14394     },
14395
14396     // private
14397     updateResponse : function(dataSet){
14398
14399     }
14400 });/*
14401  * Based on:
14402  * Ext JS Library 1.1.1
14403  * Copyright(c) 2006-2007, Ext JS, LLC.
14404  *
14405  * Originally Released Under LGPL - original licence link has changed is not relivant.
14406  *
14407  * Fork - LGPL
14408  * <script type="text/javascript">
14409  */
14410
14411 /**
14412  * @class Roo.data.ScriptTagProxy
14413  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14414  * other than the originating domain of the running page.<br><br>
14415  * <p>
14416  * <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
14417  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14418  * <p>
14419  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14420  * source code that is used as the source inside a &lt;script> tag.<br><br>
14421  * <p>
14422  * In order for the browser to process the returned data, the server must wrap the data object
14423  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14424  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14425  * depending on whether the callback name was passed:
14426  * <p>
14427  * <pre><code>
14428 boolean scriptTag = false;
14429 String cb = request.getParameter("callback");
14430 if (cb != null) {
14431     scriptTag = true;
14432     response.setContentType("text/javascript");
14433 } else {
14434     response.setContentType("application/x-json");
14435 }
14436 Writer out = response.getWriter();
14437 if (scriptTag) {
14438     out.write(cb + "(");
14439 }
14440 out.print(dataBlock.toJsonString());
14441 if (scriptTag) {
14442     out.write(");");
14443 }
14444 </pre></code>
14445  *
14446  * @constructor
14447  * @param {Object} config A configuration object.
14448  */
14449 Roo.data.ScriptTagProxy = function(config){
14450     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14451     Roo.apply(this, config);
14452     this.head = document.getElementsByTagName("head")[0];
14453 };
14454
14455 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14456
14457 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14458     /**
14459      * @cfg {String} url The URL from which to request the data object.
14460      */
14461     /**
14462      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14463      */
14464     timeout : 30000,
14465     /**
14466      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14467      * the server the name of the callback function set up by the load call to process the returned data object.
14468      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14469      * javascript output which calls this named function passing the data object as its only parameter.
14470      */
14471     callbackParam : "callback",
14472     /**
14473      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14474      * name to the request.
14475      */
14476     nocache : true,
14477
14478     /**
14479      * Load data from the configured URL, read the data object into
14480      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14481      * process that block using the passed callback.
14482      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14483      * for the request to the remote server.
14484      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14485      * object into a block of Roo.data.Records.
14486      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14487      * The function must be passed <ul>
14488      * <li>The Record block object</li>
14489      * <li>The "arg" argument from the load function</li>
14490      * <li>A boolean success indicator</li>
14491      * </ul>
14492      * @param {Object} scope The scope in which to call the callback
14493      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14494      */
14495     load : function(params, reader, callback, scope, arg){
14496         if(this.fireEvent("beforeload", this, params) !== false){
14497
14498             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14499
14500             var url = this.url;
14501             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14502             if(this.nocache){
14503                 url += "&_dc=" + (new Date().getTime());
14504             }
14505             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14506             var trans = {
14507                 id : transId,
14508                 cb : "stcCallback"+transId,
14509                 scriptId : "stcScript"+transId,
14510                 params : params,
14511                 arg : arg,
14512                 url : url,
14513                 callback : callback,
14514                 scope : scope,
14515                 reader : reader
14516             };
14517             var conn = this;
14518
14519             window[trans.cb] = function(o){
14520                 conn.handleResponse(o, trans);
14521             };
14522
14523             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14524
14525             if(this.autoAbort !== false){
14526                 this.abort();
14527             }
14528
14529             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14530
14531             var script = document.createElement("script");
14532             script.setAttribute("src", url);
14533             script.setAttribute("type", "text/javascript");
14534             script.setAttribute("id", trans.scriptId);
14535             this.head.appendChild(script);
14536
14537             this.trans = trans;
14538         }else{
14539             callback.call(scope||this, null, arg, false);
14540         }
14541     },
14542
14543     // private
14544     isLoading : function(){
14545         return this.trans ? true : false;
14546     },
14547
14548     /**
14549      * Abort the current server request.
14550      */
14551     abort : function(){
14552         if(this.isLoading()){
14553             this.destroyTrans(this.trans);
14554         }
14555     },
14556
14557     // private
14558     destroyTrans : function(trans, isLoaded){
14559         this.head.removeChild(document.getElementById(trans.scriptId));
14560         clearTimeout(trans.timeoutId);
14561         if(isLoaded){
14562             window[trans.cb] = undefined;
14563             try{
14564                 delete window[trans.cb];
14565             }catch(e){}
14566         }else{
14567             // if hasn't been loaded, wait for load to remove it to prevent script error
14568             window[trans.cb] = function(){
14569                 window[trans.cb] = undefined;
14570                 try{
14571                     delete window[trans.cb];
14572                 }catch(e){}
14573             };
14574         }
14575     },
14576
14577     // private
14578     handleResponse : function(o, trans){
14579         this.trans = false;
14580         this.destroyTrans(trans, true);
14581         var result;
14582         try {
14583             result = trans.reader.readRecords(o);
14584         }catch(e){
14585             this.fireEvent("loadexception", this, o, trans.arg, e);
14586             trans.callback.call(trans.scope||window, null, trans.arg, false);
14587             return;
14588         }
14589         this.fireEvent("load", this, o, trans.arg);
14590         trans.callback.call(trans.scope||window, result, trans.arg, true);
14591     },
14592
14593     // private
14594     handleFailure : function(trans){
14595         this.trans = false;
14596         this.destroyTrans(trans, false);
14597         this.fireEvent("loadexception", this, null, trans.arg);
14598         trans.callback.call(trans.scope||window, null, trans.arg, false);
14599     }
14600 });/*
14601  * Based on:
14602  * Ext JS Library 1.1.1
14603  * Copyright(c) 2006-2007, Ext JS, LLC.
14604  *
14605  * Originally Released Under LGPL - original licence link has changed is not relivant.
14606  *
14607  * Fork - LGPL
14608  * <script type="text/javascript">
14609  */
14610
14611 /**
14612  * @class Roo.data.JsonReader
14613  * @extends Roo.data.DataReader
14614  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14615  * based on mappings in a provided Roo.data.Record constructor.
14616  * 
14617  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14618  * in the reply previously. 
14619  * 
14620  * <p>
14621  * Example code:
14622  * <pre><code>
14623 var RecordDef = Roo.data.Record.create([
14624     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14625     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14626 ]);
14627 var myReader = new Roo.data.JsonReader({
14628     totalProperty: "results",    // The property which contains the total dataset size (optional)
14629     root: "rows",                // The property which contains an Array of row objects
14630     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14631 }, RecordDef);
14632 </code></pre>
14633  * <p>
14634  * This would consume a JSON file like this:
14635  * <pre><code>
14636 { 'results': 2, 'rows': [
14637     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14638     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14639 }
14640 </code></pre>
14641  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14642  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14643  * paged from the remote server.
14644  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14645  * @cfg {String} root name of the property which contains the Array of row objects.
14646  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14647  * @cfg {Array} fields Array of field definition objects
14648  * @constructor
14649  * Create a new JsonReader
14650  * @param {Object} meta Metadata configuration options
14651  * @param {Object} recordType Either an Array of field definition objects,
14652  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14653  */
14654 Roo.data.JsonReader = function(meta, recordType){
14655     
14656     meta = meta || {};
14657     // set some defaults:
14658     Roo.applyIf(meta, {
14659         totalProperty: 'total',
14660         successProperty : 'success',
14661         root : 'data',
14662         id : 'id'
14663     });
14664     
14665     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14666 };
14667 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14668     
14669     readerType : 'Json',
14670     
14671     /**
14672      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14673      * Used by Store query builder to append _requestMeta to params.
14674      * 
14675      */
14676     metaFromRemote : false,
14677     /**
14678      * This method is only used by a DataProxy which has retrieved data from a remote server.
14679      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14680      * @return {Object} data A data block which is used by an Roo.data.Store object as
14681      * a cache of Roo.data.Records.
14682      */
14683     read : function(response){
14684         var json = response.responseText;
14685        
14686         var o = /* eval:var:o */ eval("("+json+")");
14687         if(!o) {
14688             throw {message: "JsonReader.read: Json object not found"};
14689         }
14690         
14691         if(o.metaData){
14692             
14693             delete this.ef;
14694             this.metaFromRemote = true;
14695             this.meta = o.metaData;
14696             this.recordType = Roo.data.Record.create(o.metaData.fields);
14697             this.onMetaChange(this.meta, this.recordType, o);
14698         }
14699         return this.readRecords(o);
14700     },
14701
14702     // private function a store will implement
14703     onMetaChange : function(meta, recordType, o){
14704
14705     },
14706
14707     /**
14708          * @ignore
14709          */
14710     simpleAccess: function(obj, subsc) {
14711         return obj[subsc];
14712     },
14713
14714         /**
14715          * @ignore
14716          */
14717     getJsonAccessor: function(){
14718         var re = /[\[\.]/;
14719         return function(expr) {
14720             try {
14721                 return(re.test(expr))
14722                     ? new Function("obj", "return obj." + expr)
14723                     : function(obj){
14724                         return obj[expr];
14725                     };
14726             } catch(e){}
14727             return Roo.emptyFn;
14728         };
14729     }(),
14730
14731     /**
14732      * Create a data block containing Roo.data.Records from an XML document.
14733      * @param {Object} o An object which contains an Array of row objects in the property specified
14734      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14735      * which contains the total size of the dataset.
14736      * @return {Object} data A data block which is used by an Roo.data.Store object as
14737      * a cache of Roo.data.Records.
14738      */
14739     readRecords : function(o){
14740         /**
14741          * After any data loads, the raw JSON data is available for further custom processing.
14742          * @type Object
14743          */
14744         this.o = o;
14745         var s = this.meta, Record = this.recordType,
14746             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14747
14748 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14749         if (!this.ef) {
14750             if(s.totalProperty) {
14751                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14752                 }
14753                 if(s.successProperty) {
14754                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14755                 }
14756                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14757                 if (s.id) {
14758                         var g = this.getJsonAccessor(s.id);
14759                         this.getId = function(rec) {
14760                                 var r = g(rec);  
14761                                 return (r === undefined || r === "") ? null : r;
14762                         };
14763                 } else {
14764                         this.getId = function(){return null;};
14765                 }
14766             this.ef = [];
14767             for(var jj = 0; jj < fl; jj++){
14768                 f = fi[jj];
14769                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14770                 this.ef[jj] = this.getJsonAccessor(map);
14771             }
14772         }
14773
14774         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14775         if(s.totalProperty){
14776             var vt = parseInt(this.getTotal(o), 10);
14777             if(!isNaN(vt)){
14778                 totalRecords = vt;
14779             }
14780         }
14781         if(s.successProperty){
14782             var vs = this.getSuccess(o);
14783             if(vs === false || vs === 'false'){
14784                 success = false;
14785             }
14786         }
14787         var records = [];
14788         for(var i = 0; i < c; i++){
14789                 var n = root[i];
14790             var values = {};
14791             var id = this.getId(n);
14792             for(var j = 0; j < fl; j++){
14793                 f = fi[j];
14794             var v = this.ef[j](n);
14795             if (!f.convert) {
14796                 Roo.log('missing convert for ' + f.name);
14797                 Roo.log(f);
14798                 continue;
14799             }
14800             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14801             }
14802             var record = new Record(values, id);
14803             record.json = n;
14804             records[i] = record;
14805         }
14806         return {
14807             raw : o,
14808             success : success,
14809             records : records,
14810             totalRecords : totalRecords
14811         };
14812     },
14813     // used when loading children.. @see loadDataFromChildren
14814     toLoadData: function(rec)
14815     {
14816         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14817         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14818         return { data : data, total : data.length };
14819         
14820     }
14821 });/*
14822  * Based on:
14823  * Ext JS Library 1.1.1
14824  * Copyright(c) 2006-2007, Ext JS, LLC.
14825  *
14826  * Originally Released Under LGPL - original licence link has changed is not relivant.
14827  *
14828  * Fork - LGPL
14829  * <script type="text/javascript">
14830  */
14831
14832 /**
14833  * @class Roo.data.ArrayReader
14834  * @extends Roo.data.DataReader
14835  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14836  * Each element of that Array represents a row of data fields. The
14837  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14838  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14839  * <p>
14840  * Example code:.
14841  * <pre><code>
14842 var RecordDef = Roo.data.Record.create([
14843     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14844     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14845 ]);
14846 var myReader = new Roo.data.ArrayReader({
14847     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14848 }, RecordDef);
14849 </code></pre>
14850  * <p>
14851  * This would consume an Array like this:
14852  * <pre><code>
14853 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14854   </code></pre>
14855  
14856  * @constructor
14857  * Create a new JsonReader
14858  * @param {Object} meta Metadata configuration options.
14859  * @param {Object|Array} recordType Either an Array of field definition objects
14860  * 
14861  * @cfg {Array} fields Array of field definition objects
14862  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14863  * as specified to {@link Roo.data.Record#create},
14864  * or an {@link Roo.data.Record} object
14865  *
14866  * 
14867  * created using {@link Roo.data.Record#create}.
14868  */
14869 Roo.data.ArrayReader = function(meta, recordType)
14870 {    
14871     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14872 };
14873
14874 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14875     
14876       /**
14877      * Create a data block containing Roo.data.Records from an XML document.
14878      * @param {Object} o An Array of row objects which represents the dataset.
14879      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14880      * a cache of Roo.data.Records.
14881      */
14882     readRecords : function(o)
14883     {
14884         var sid = this.meta ? this.meta.id : null;
14885         var recordType = this.recordType, fields = recordType.prototype.fields;
14886         var records = [];
14887         var root = o;
14888         for(var i = 0; i < root.length; i++){
14889                 var n = root[i];
14890             var values = {};
14891             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14892             for(var j = 0, jlen = fields.length; j < jlen; j++){
14893                 var f = fields.items[j];
14894                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14895                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14896                 v = f.convert(v);
14897                 values[f.name] = v;
14898             }
14899             var record = new recordType(values, id);
14900             record.json = n;
14901             records[records.length] = record;
14902         }
14903         return {
14904             records : records,
14905             totalRecords : records.length
14906         };
14907     },
14908     // used when loading children.. @see loadDataFromChildren
14909     toLoadData: function(rec)
14910     {
14911         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14912         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14913         
14914     }
14915     
14916     
14917 });/*
14918  * - LGPL
14919  * * 
14920  */
14921
14922 /**
14923  * @class Roo.bootstrap.ComboBox
14924  * @extends Roo.bootstrap.TriggerField
14925  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14926  * @cfg {Boolean} append (true|false) default false
14927  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14928  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14929  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14930  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14931  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14932  * @cfg {Boolean} animate default true
14933  * @cfg {Boolean} emptyResultText only for touch device
14934  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14935  * @cfg {String} emptyTitle default ''
14936  * @cfg {Number} width fixed with? experimental
14937  * @constructor
14938  * Create a new ComboBox.
14939  * @param {Object} config Configuration options
14940  */
14941 Roo.bootstrap.ComboBox = function(config){
14942     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14943     this.addEvents({
14944         /**
14945          * @event expand
14946          * Fires when the dropdown list is expanded
14947         * @param {Roo.bootstrap.ComboBox} combo This combo box
14948         */
14949         'expand' : true,
14950         /**
14951          * @event collapse
14952          * Fires when the dropdown list is collapsed
14953         * @param {Roo.bootstrap.ComboBox} combo This combo box
14954         */
14955         'collapse' : true,
14956         /**
14957          * @event beforeselect
14958          * Fires before a list item is selected. Return false to cancel the selection.
14959         * @param {Roo.bootstrap.ComboBox} combo This combo box
14960         * @param {Roo.data.Record} record The data record returned from the underlying store
14961         * @param {Number} index The index of the selected item in the dropdown list
14962         */
14963         'beforeselect' : true,
14964         /**
14965          * @event select
14966          * Fires when a list item is selected
14967         * @param {Roo.bootstrap.ComboBox} combo This combo box
14968         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14969         * @param {Number} index The index of the selected item in the dropdown list
14970         */
14971         'select' : true,
14972         /**
14973          * @event beforequery
14974          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14975          * The event object passed has these properties:
14976         * @param {Roo.bootstrap.ComboBox} combo This combo box
14977         * @param {String} query The query
14978         * @param {Boolean} forceAll true to force "all" query
14979         * @param {Boolean} cancel true to cancel the query
14980         * @param {Object} e The query event object
14981         */
14982         'beforequery': true,
14983          /**
14984          * @event add
14985          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14986         * @param {Roo.bootstrap.ComboBox} combo This combo box
14987         */
14988         'add' : true,
14989         /**
14990          * @event edit
14991          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14992         * @param {Roo.bootstrap.ComboBox} combo This combo box
14993         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14994         */
14995         'edit' : true,
14996         /**
14997          * @event remove
14998          * Fires when the remove value from the combobox array
14999         * @param {Roo.bootstrap.ComboBox} combo This combo box
15000         */
15001         'remove' : true,
15002         /**
15003          * @event afterremove
15004          * Fires when the remove value from the combobox array
15005         * @param {Roo.bootstrap.ComboBox} combo This combo box
15006         */
15007         'afterremove' : true,
15008         /**
15009          * @event specialfilter
15010          * Fires when specialfilter
15011             * @param {Roo.bootstrap.ComboBox} combo This combo box
15012             */
15013         'specialfilter' : true,
15014         /**
15015          * @event tick
15016          * Fires when tick the element
15017             * @param {Roo.bootstrap.ComboBox} combo This combo box
15018             */
15019         'tick' : true,
15020         /**
15021          * @event touchviewdisplay
15022          * Fires when touch view require special display (default is using displayField)
15023             * @param {Roo.bootstrap.ComboBox} combo This combo box
15024             * @param {Object} cfg set html .
15025             */
15026         'touchviewdisplay' : true
15027         
15028     });
15029     
15030     this.item = [];
15031     this.tickItems = [];
15032     
15033     this.selectedIndex = -1;
15034     if(this.mode == 'local'){
15035         if(config.queryDelay === undefined){
15036             this.queryDelay = 10;
15037         }
15038         if(config.minChars === undefined){
15039             this.minChars = 0;
15040         }
15041     }
15042 };
15043
15044 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15045      
15046     /**
15047      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15048      * rendering into an Roo.Editor, defaults to false)
15049      */
15050     /**
15051      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15052      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15053      */
15054     /**
15055      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15056      */
15057     /**
15058      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15059      * the dropdown list (defaults to undefined, with no header element)
15060      */
15061
15062      /**
15063      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15064      */
15065      
15066      /**
15067      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15068      */
15069     listWidth: undefined,
15070     /**
15071      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15072      * mode = 'remote' or 'text' if mode = 'local')
15073      */
15074     displayField: undefined,
15075     
15076     /**
15077      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15078      * mode = 'remote' or 'value' if mode = 'local'). 
15079      * Note: use of a valueField requires the user make a selection
15080      * in order for a value to be mapped.
15081      */
15082     valueField: undefined,
15083     /**
15084      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15085      */
15086     modalTitle : '',
15087     
15088     /**
15089      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15090      * field's data value (defaults to the underlying DOM element's name)
15091      */
15092     hiddenName: undefined,
15093     /**
15094      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15095      */
15096     listClass: '',
15097     /**
15098      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15099      */
15100     selectedClass: 'active',
15101     
15102     /**
15103      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15104      */
15105     shadow:'sides',
15106     /**
15107      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15108      * anchor positions (defaults to 'tl-bl')
15109      */
15110     listAlign: 'tl-bl?',
15111     /**
15112      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15113      */
15114     maxHeight: 300,
15115     /**
15116      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15117      * query specified by the allQuery config option (defaults to 'query')
15118      */
15119     triggerAction: 'query',
15120     /**
15121      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15122      * (defaults to 4, does not apply if editable = false)
15123      */
15124     minChars : 4,
15125     /**
15126      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15127      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15128      */
15129     typeAhead: false,
15130     /**
15131      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15132      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15133      */
15134     queryDelay: 500,
15135     /**
15136      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15137      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15138      */
15139     pageSize: 0,
15140     /**
15141      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15142      * when editable = true (defaults to false)
15143      */
15144     selectOnFocus:false,
15145     /**
15146      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15147      */
15148     queryParam: 'query',
15149     /**
15150      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15151      * when mode = 'remote' (defaults to 'Loading...')
15152      */
15153     loadingText: 'Loading...',
15154     /**
15155      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15156      */
15157     resizable: false,
15158     /**
15159      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15160      */
15161     handleHeight : 8,
15162     /**
15163      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15164      * traditional select (defaults to true)
15165      */
15166     editable: true,
15167     /**
15168      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15169      */
15170     allQuery: '',
15171     /**
15172      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15173      */
15174     mode: 'remote',
15175     /**
15176      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15177      * listWidth has a higher value)
15178      */
15179     minListWidth : 70,
15180     /**
15181      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15182      * allow the user to set arbitrary text into the field (defaults to false)
15183      */
15184     forceSelection:false,
15185     /**
15186      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15187      * if typeAhead = true (defaults to 250)
15188      */
15189     typeAheadDelay : 250,
15190     /**
15191      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15192      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15193      */
15194     valueNotFoundText : undefined,
15195     /**
15196      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15197      */
15198     blockFocus : false,
15199     
15200     /**
15201      * @cfg {Boolean} disableClear Disable showing of clear button.
15202      */
15203     disableClear : false,
15204     /**
15205      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15206      */
15207     alwaysQuery : false,
15208     
15209     /**
15210      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15211      */
15212     multiple : false,
15213     
15214     /**
15215      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15216      */
15217     invalidClass : "has-warning",
15218     
15219     /**
15220      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15221      */
15222     validClass : "has-success",
15223     
15224     /**
15225      * @cfg {Boolean} specialFilter (true|false) special filter default false
15226      */
15227     specialFilter : false,
15228     
15229     /**
15230      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15231      */
15232     mobileTouchView : true,
15233     
15234     /**
15235      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15236      */
15237     useNativeIOS : false,
15238     
15239     /**
15240      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15241      */
15242     mobile_restrict_height : false,
15243     
15244     ios_options : false,
15245     
15246     //private
15247     addicon : false,
15248     editicon: false,
15249     
15250     page: 0,
15251     hasQuery: false,
15252     append: false,
15253     loadNext: false,
15254     autoFocus : true,
15255     tickable : false,
15256     btnPosition : 'right',
15257     triggerList : true,
15258     showToggleBtn : true,
15259     animate : true,
15260     emptyResultText: 'Empty',
15261     triggerText : 'Select',
15262     emptyTitle : '',
15263     width : false,
15264     
15265     // element that contains real text value.. (when hidden is used..)
15266     
15267     getAutoCreate : function()
15268     {   
15269         var cfg = false;
15270         //render
15271         /*
15272          * Render classic select for iso
15273          */
15274         
15275         if(Roo.isIOS && this.useNativeIOS){
15276             cfg = this.getAutoCreateNativeIOS();
15277             return cfg;
15278         }
15279         
15280         /*
15281          * Touch Devices
15282          */
15283         
15284         if(Roo.isTouch && this.mobileTouchView){
15285             cfg = this.getAutoCreateTouchView();
15286             return cfg;;
15287         }
15288         
15289         /*
15290          *  Normal ComboBox
15291          */
15292         if(!this.tickable){
15293             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15294             return cfg;
15295         }
15296         
15297         /*
15298          *  ComboBox with tickable selections
15299          */
15300              
15301         var align = this.labelAlign || this.parentLabelAlign();
15302         
15303         cfg = {
15304             cls : 'form-group roo-combobox-tickable' //input-group
15305         };
15306         
15307         var btn_text_select = '';
15308         var btn_text_done = '';
15309         var btn_text_cancel = '';
15310         
15311         if (this.btn_text_show) {
15312             btn_text_select = 'Select';
15313             btn_text_done = 'Done';
15314             btn_text_cancel = 'Cancel'; 
15315         }
15316         
15317         var buttons = {
15318             tag : 'div',
15319             cls : 'tickable-buttons',
15320             cn : [
15321                 {
15322                     tag : 'button',
15323                     type : 'button',
15324                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15325                     //html : this.triggerText
15326                     html: btn_text_select
15327                 },
15328                 {
15329                     tag : 'button',
15330                     type : 'button',
15331                     name : 'ok',
15332                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15333                     //html : 'Done'
15334                     html: btn_text_done
15335                 },
15336                 {
15337                     tag : 'button',
15338                     type : 'button',
15339                     name : 'cancel',
15340                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15341                     //html : 'Cancel'
15342                     html: btn_text_cancel
15343                 }
15344             ]
15345         };
15346         
15347         if(this.editable){
15348             buttons.cn.unshift({
15349                 tag: 'input',
15350                 cls: 'roo-select2-search-field-input'
15351             });
15352         }
15353         
15354         var _this = this;
15355         
15356         Roo.each(buttons.cn, function(c){
15357             if (_this.size) {
15358                 c.cls += ' btn-' + _this.size;
15359             }
15360
15361             if (_this.disabled) {
15362                 c.disabled = true;
15363             }
15364         });
15365         
15366         var box = {
15367             tag: 'div',
15368             style : 'display: contents',
15369             cn: [
15370                 {
15371                     tag: 'input',
15372                     type : 'hidden',
15373                     cls: 'form-hidden-field'
15374                 },
15375                 {
15376                     tag: 'ul',
15377                     cls: 'roo-select2-choices',
15378                     cn:[
15379                         {
15380                             tag: 'li',
15381                             cls: 'roo-select2-search-field',
15382                             cn: [
15383                                 buttons
15384                             ]
15385                         }
15386                     ]
15387                 }
15388             ]
15389         };
15390         
15391         var combobox = {
15392             cls: 'roo-select2-container input-group roo-select2-container-multi',
15393             cn: [
15394                 
15395                 box
15396 //                {
15397 //                    tag: 'ul',
15398 //                    cls: 'typeahead typeahead-long dropdown-menu',
15399 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15400 //                }
15401             ]
15402         };
15403         
15404         if(this.hasFeedback && !this.allowBlank){
15405             
15406             var feedback = {
15407                 tag: 'span',
15408                 cls: 'glyphicon form-control-feedback'
15409             };
15410
15411             combobox.cn.push(feedback);
15412         }
15413         
15414         
15415         
15416         var indicator = {
15417             tag : 'i',
15418             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15419             tooltip : 'This field is required'
15420         };
15421         if (Roo.bootstrap.version == 4) {
15422             indicator = {
15423                 tag : 'i',
15424                 style : 'display:none'
15425             };
15426         }
15427         if (align ==='left' && this.fieldLabel.length) {
15428             
15429             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15430             
15431             cfg.cn = [
15432                 indicator,
15433                 {
15434                     tag: 'label',
15435                     'for' :  id,
15436                     cls : 'control-label col-form-label',
15437                     html : this.fieldLabel
15438
15439                 },
15440                 {
15441                     cls : "", 
15442                     cn: [
15443                         combobox
15444                     ]
15445                 }
15446
15447             ];
15448             
15449             var labelCfg = cfg.cn[1];
15450             var contentCfg = cfg.cn[2];
15451             
15452
15453             if(this.indicatorpos == 'right'){
15454                 
15455                 cfg.cn = [
15456                     {
15457                         tag: 'label',
15458                         'for' :  id,
15459                         cls : 'control-label col-form-label',
15460                         cn : [
15461                             {
15462                                 tag : 'span',
15463                                 html : this.fieldLabel
15464                             },
15465                             indicator
15466                         ]
15467                     },
15468                     {
15469                         cls : "",
15470                         cn: [
15471                             combobox
15472                         ]
15473                     }
15474
15475                 ];
15476                 
15477                 
15478                 
15479                 labelCfg = cfg.cn[0];
15480                 contentCfg = cfg.cn[1];
15481             
15482             }
15483             
15484             if(this.labelWidth > 12){
15485                 labelCfg.style = "width: " + this.labelWidth + 'px';
15486             }
15487             if(this.width * 1 > 0){
15488                 contentCfg.style = "width: " + this.width + 'px';
15489             }
15490             if(this.labelWidth < 13 && this.labelmd == 0){
15491                 this.labelmd = this.labelWidth;
15492             }
15493             
15494             if(this.labellg > 0){
15495                 labelCfg.cls += ' col-lg-' + this.labellg;
15496                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15497             }
15498             
15499             if(this.labelmd > 0){
15500                 labelCfg.cls += ' col-md-' + this.labelmd;
15501                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15502             }
15503             
15504             if(this.labelsm > 0){
15505                 labelCfg.cls += ' col-sm-' + this.labelsm;
15506                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15507             }
15508             
15509             if(this.labelxs > 0){
15510                 labelCfg.cls += ' col-xs-' + this.labelxs;
15511                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15512             }
15513                 
15514                 
15515         } else if ( this.fieldLabel.length) {
15516 //                Roo.log(" label");
15517                  cfg.cn = [
15518                    indicator,
15519                     {
15520                         tag: 'label',
15521                         //cls : 'input-group-addon',
15522                         html : this.fieldLabel
15523                     },
15524                     combobox
15525                 ];
15526                 
15527                 if(this.indicatorpos == 'right'){
15528                     cfg.cn = [
15529                         {
15530                             tag: 'label',
15531                             //cls : 'input-group-addon',
15532                             html : this.fieldLabel
15533                         },
15534                         indicator,
15535                         combobox
15536                     ];
15537                     
15538                 }
15539
15540         } else {
15541             
15542 //                Roo.log(" no label && no align");
15543                 cfg = combobox
15544                      
15545                 
15546         }
15547          
15548         var settings=this;
15549         ['xs','sm','md','lg'].map(function(size){
15550             if (settings[size]) {
15551                 cfg.cls += ' col-' + size + '-' + settings[size];
15552             }
15553         });
15554         
15555         return cfg;
15556         
15557     },
15558     
15559     _initEventsCalled : false,
15560     
15561     // private
15562     initEvents: function()
15563     {   
15564         if (this._initEventsCalled) { // as we call render... prevent looping...
15565             return;
15566         }
15567         this._initEventsCalled = true;
15568         
15569         if (!this.store) {
15570             throw "can not find store for combo";
15571         }
15572         
15573         this.indicator = this.indicatorEl();
15574         
15575         this.store = Roo.factory(this.store, Roo.data);
15576         this.store.parent = this;
15577         
15578         // if we are building from html. then this element is so complex, that we can not really
15579         // use the rendered HTML.
15580         // so we have to trash and replace the previous code.
15581         if (Roo.XComponent.build_from_html) {
15582             // remove this element....
15583             var e = this.el.dom, k=0;
15584             while (e ) { e = e.previousSibling;  ++k;}
15585
15586             this.el.remove();
15587             
15588             this.el=false;
15589             this.rendered = false;
15590             
15591             this.render(this.parent().getChildContainer(true), k);
15592         }
15593         
15594         if(Roo.isIOS && this.useNativeIOS){
15595             this.initIOSView();
15596             return;
15597         }
15598         
15599         /*
15600          * Touch Devices
15601          */
15602         
15603         if(Roo.isTouch && this.mobileTouchView){
15604             this.initTouchView();
15605             return;
15606         }
15607         
15608         if(this.tickable){
15609             this.initTickableEvents();
15610             return;
15611         }
15612         
15613         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15614         
15615         if(this.hiddenName){
15616             
15617             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15618             
15619             this.hiddenField.dom.value =
15620                 this.hiddenValue !== undefined ? this.hiddenValue :
15621                 this.value !== undefined ? this.value : '';
15622
15623             // prevent input submission
15624             this.el.dom.removeAttribute('name');
15625             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15626              
15627              
15628         }
15629         //if(Roo.isGecko){
15630         //    this.el.dom.setAttribute('autocomplete', 'off');
15631         //}
15632         
15633         var cls = 'x-combo-list';
15634         
15635         //this.list = new Roo.Layer({
15636         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15637         //});
15638         
15639         var _this = this;
15640         
15641         (function(){
15642             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15643             _this.list.setWidth(lw);
15644         }).defer(100);
15645         
15646         this.list.on('mouseover', this.onViewOver, this);
15647         this.list.on('mousemove', this.onViewMove, this);
15648         this.list.on('scroll', this.onViewScroll, this);
15649         
15650         /*
15651         this.list.swallowEvent('mousewheel');
15652         this.assetHeight = 0;
15653
15654         if(this.title){
15655             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15656             this.assetHeight += this.header.getHeight();
15657         }
15658
15659         this.innerList = this.list.createChild({cls:cls+'-inner'});
15660         this.innerList.on('mouseover', this.onViewOver, this);
15661         this.innerList.on('mousemove', this.onViewMove, this);
15662         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15663         
15664         if(this.allowBlank && !this.pageSize && !this.disableClear){
15665             this.footer = this.list.createChild({cls:cls+'-ft'});
15666             this.pageTb = new Roo.Toolbar(this.footer);
15667            
15668         }
15669         if(this.pageSize){
15670             this.footer = this.list.createChild({cls:cls+'-ft'});
15671             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15672                     {pageSize: this.pageSize});
15673             
15674         }
15675         
15676         if (this.pageTb && this.allowBlank && !this.disableClear) {
15677             var _this = this;
15678             this.pageTb.add(new Roo.Toolbar.Fill(), {
15679                 cls: 'x-btn-icon x-btn-clear',
15680                 text: '&#160;',
15681                 handler: function()
15682                 {
15683                     _this.collapse();
15684                     _this.clearValue();
15685                     _this.onSelect(false, -1);
15686                 }
15687             });
15688         }
15689         if (this.footer) {
15690             this.assetHeight += this.footer.getHeight();
15691         }
15692         */
15693             
15694         if(!this.tpl){
15695             this.tpl = Roo.bootstrap.version == 4 ?
15696                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15697                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15698         }
15699
15700         this.view = new Roo.View(this.list, this.tpl, {
15701             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15702         });
15703         //this.view.wrapEl.setDisplayed(false);
15704         this.view.on('click', this.onViewClick, this);
15705         
15706         
15707         this.store.on('beforeload', this.onBeforeLoad, this);
15708         this.store.on('load', this.onLoad, this);
15709         this.store.on('loadexception', this.onLoadException, this);
15710         /*
15711         if(this.resizable){
15712             this.resizer = new Roo.Resizable(this.list,  {
15713                pinned:true, handles:'se'
15714             });
15715             this.resizer.on('resize', function(r, w, h){
15716                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15717                 this.listWidth = w;
15718                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15719                 this.restrictHeight();
15720             }, this);
15721             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15722         }
15723         */
15724         if(!this.editable){
15725             this.editable = true;
15726             this.setEditable(false);
15727         }
15728         
15729         /*
15730         
15731         if (typeof(this.events.add.listeners) != 'undefined') {
15732             
15733             this.addicon = this.wrap.createChild(
15734                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15735        
15736             this.addicon.on('click', function(e) {
15737                 this.fireEvent('add', this);
15738             }, this);
15739         }
15740         if (typeof(this.events.edit.listeners) != 'undefined') {
15741             
15742             this.editicon = this.wrap.createChild(
15743                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15744             if (this.addicon) {
15745                 this.editicon.setStyle('margin-left', '40px');
15746             }
15747             this.editicon.on('click', function(e) {
15748                 
15749                 // we fire even  if inothing is selected..
15750                 this.fireEvent('edit', this, this.lastData );
15751                 
15752             }, this);
15753         }
15754         */
15755         
15756         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15757             "up" : function(e){
15758                 this.inKeyMode = true;
15759                 this.selectPrev();
15760             },
15761
15762             "down" : function(e){
15763                 if(!this.isExpanded()){
15764                     this.onTriggerClick();
15765                 }else{
15766                     this.inKeyMode = true;
15767                     this.selectNext();
15768                 }
15769             },
15770
15771             "enter" : function(e){
15772 //                this.onViewClick();
15773                 //return true;
15774                 this.collapse();
15775                 
15776                 if(this.fireEvent("specialkey", this, e)){
15777                     this.onViewClick(false);
15778                 }
15779                 
15780                 return true;
15781             },
15782
15783             "esc" : function(e){
15784                 this.collapse();
15785             },
15786
15787             "tab" : function(e){
15788                 this.collapse();
15789                 
15790                 if(this.fireEvent("specialkey", this, e)){
15791                     this.onViewClick(false);
15792                 }
15793                 
15794                 return true;
15795             },
15796
15797             scope : this,
15798
15799             doRelay : function(foo, bar, hname){
15800                 if(hname == 'down' || this.scope.isExpanded()){
15801                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15802                 }
15803                 return true;
15804             },
15805
15806             forceKeyDown: true
15807         });
15808         
15809         
15810         this.queryDelay = Math.max(this.queryDelay || 10,
15811                 this.mode == 'local' ? 10 : 250);
15812         
15813         
15814         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15815         
15816         if(this.typeAhead){
15817             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15818         }
15819         if(this.editable !== false){
15820             this.inputEl().on("keyup", this.onKeyUp, this);
15821         }
15822         if(this.forceSelection){
15823             this.inputEl().on('blur', this.doForce, this);
15824         }
15825         
15826         if(this.multiple){
15827             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15828             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15829         }
15830     },
15831     
15832     initTickableEvents: function()
15833     {   
15834         this.createList();
15835         
15836         if(this.hiddenName){
15837             
15838             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15839             
15840             this.hiddenField.dom.value =
15841                 this.hiddenValue !== undefined ? this.hiddenValue :
15842                 this.value !== undefined ? this.value : '';
15843
15844             // prevent input submission
15845             this.el.dom.removeAttribute('name');
15846             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15847              
15848              
15849         }
15850         
15851 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15852         
15853         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15854         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15855         if(this.triggerList){
15856             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15857         }
15858          
15859         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15860         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15861         
15862         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15863         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15864         
15865         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15866         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15867         
15868         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15869         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15870         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15871         
15872         this.okBtn.hide();
15873         this.cancelBtn.hide();
15874         
15875         var _this = this;
15876         
15877         (function(){
15878             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15879             _this.list.setWidth(lw);
15880         }).defer(100);
15881         
15882         this.list.on('mouseover', this.onViewOver, this);
15883         this.list.on('mousemove', this.onViewMove, this);
15884         
15885         this.list.on('scroll', this.onViewScroll, this);
15886         
15887         if(!this.tpl){
15888             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15889                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15890         }
15891
15892         this.view = new Roo.View(this.list, this.tpl, {
15893             singleSelect:true,
15894             tickable:true,
15895             parent:this,
15896             store: this.store,
15897             selectedClass: this.selectedClass
15898         });
15899         
15900         //this.view.wrapEl.setDisplayed(false);
15901         this.view.on('click', this.onViewClick, this);
15902         
15903         
15904         
15905         this.store.on('beforeload', this.onBeforeLoad, this);
15906         this.store.on('load', this.onLoad, this);
15907         this.store.on('loadexception', this.onLoadException, this);
15908         
15909         if(this.editable){
15910             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15911                 "up" : function(e){
15912                     this.inKeyMode = true;
15913                     this.selectPrev();
15914                 },
15915
15916                 "down" : function(e){
15917                     this.inKeyMode = true;
15918                     this.selectNext();
15919                 },
15920
15921                 "enter" : function(e){
15922                     if(this.fireEvent("specialkey", this, e)){
15923                         this.onViewClick(false);
15924                     }
15925                     
15926                     return true;
15927                 },
15928
15929                 "esc" : function(e){
15930                     this.onTickableFooterButtonClick(e, false, false);
15931                 },
15932
15933                 "tab" : function(e){
15934                     this.fireEvent("specialkey", this, e);
15935                     
15936                     this.onTickableFooterButtonClick(e, false, false);
15937                     
15938                     return true;
15939                 },
15940
15941                 scope : this,
15942
15943                 doRelay : function(e, fn, key){
15944                     if(this.scope.isExpanded()){
15945                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15946                     }
15947                     return true;
15948                 },
15949
15950                 forceKeyDown: true
15951             });
15952         }
15953         
15954         this.queryDelay = Math.max(this.queryDelay || 10,
15955                 this.mode == 'local' ? 10 : 250);
15956         
15957         
15958         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15959         
15960         if(this.typeAhead){
15961             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15962         }
15963         
15964         if(this.editable !== false){
15965             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15966         }
15967         
15968         this.indicator = this.indicatorEl();
15969         
15970         if(this.indicator){
15971             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15972             this.indicator.hide();
15973         }
15974         
15975     },
15976
15977     onDestroy : function(){
15978         if(this.view){
15979             this.view.setStore(null);
15980             this.view.el.removeAllListeners();
15981             this.view.el.remove();
15982             this.view.purgeListeners();
15983         }
15984         if(this.list){
15985             this.list.dom.innerHTML  = '';
15986         }
15987         
15988         if(this.store){
15989             this.store.un('beforeload', this.onBeforeLoad, this);
15990             this.store.un('load', this.onLoad, this);
15991             this.store.un('loadexception', this.onLoadException, this);
15992         }
15993         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15994     },
15995
15996     // private
15997     fireKey : function(e){
15998         if(e.isNavKeyPress() && !this.list.isVisible()){
15999             this.fireEvent("specialkey", this, e);
16000         }
16001     },
16002
16003     // private
16004     onResize: function(w, h)
16005     {
16006         
16007         
16008 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16009 //        
16010 //        if(typeof w != 'number'){
16011 //            // we do not handle it!?!?
16012 //            return;
16013 //        }
16014 //        var tw = this.trigger.getWidth();
16015 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16016 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16017 //        var x = w - tw;
16018 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16019 //            
16020 //        //this.trigger.setStyle('left', x+'px');
16021 //        
16022 //        if(this.list && this.listWidth === undefined){
16023 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16024 //            this.list.setWidth(lw);
16025 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16026 //        }
16027         
16028     
16029         
16030     },
16031
16032     /**
16033      * Allow or prevent the user from directly editing the field text.  If false is passed,
16034      * the user will only be able to select from the items defined in the dropdown list.  This method
16035      * is the runtime equivalent of setting the 'editable' config option at config time.
16036      * @param {Boolean} value True to allow the user to directly edit the field text
16037      */
16038     setEditable : function(value){
16039         if(value == this.editable){
16040             return;
16041         }
16042         this.editable = value;
16043         if(!value){
16044             this.inputEl().dom.setAttribute('readOnly', true);
16045             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16046             this.inputEl().addClass('x-combo-noedit');
16047         }else{
16048             this.inputEl().dom.setAttribute('readOnly', false);
16049             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16050             this.inputEl().removeClass('x-combo-noedit');
16051         }
16052     },
16053
16054     // private
16055     
16056     onBeforeLoad : function(combo,opts){
16057         if(!this.hasFocus){
16058             return;
16059         }
16060          if (!opts.add) {
16061             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16062          }
16063         this.restrictHeight();
16064         this.selectedIndex = -1;
16065     },
16066
16067     // private
16068     onLoad : function(){
16069         
16070         this.hasQuery = false;
16071         
16072         if(!this.hasFocus){
16073             return;
16074         }
16075         
16076         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16077             this.loading.hide();
16078         }
16079         
16080         if(this.store.getCount() > 0){
16081             
16082             this.expand();
16083             this.restrictHeight();
16084             if(this.lastQuery == this.allQuery){
16085                 if(this.editable && !this.tickable){
16086                     this.inputEl().dom.select();
16087                 }
16088                 
16089                 if(
16090                     !this.selectByValue(this.value, true) &&
16091                     this.autoFocus && 
16092                     (
16093                         !this.store.lastOptions ||
16094                         typeof(this.store.lastOptions.add) == 'undefined' || 
16095                         this.store.lastOptions.add != true
16096                     )
16097                 ){
16098                     this.select(0, true);
16099                 }
16100             }else{
16101                 if(this.autoFocus){
16102                     this.selectNext();
16103                 }
16104                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16105                     this.taTask.delay(this.typeAheadDelay);
16106                 }
16107             }
16108         }else{
16109             this.onEmptyResults();
16110         }
16111         
16112         //this.el.focus();
16113     },
16114     // private
16115     onLoadException : function()
16116     {
16117         this.hasQuery = false;
16118         
16119         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16120             this.loading.hide();
16121         }
16122         
16123         if(this.tickable && this.editable){
16124             return;
16125         }
16126         
16127         this.collapse();
16128         // only causes errors at present
16129         //Roo.log(this.store.reader.jsonData);
16130         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16131             // fixme
16132             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16133         //}
16134         
16135         
16136     },
16137     // private
16138     onTypeAhead : function(){
16139         if(this.store.getCount() > 0){
16140             var r = this.store.getAt(0);
16141             var newValue = r.data[this.displayField];
16142             var len = newValue.length;
16143             var selStart = this.getRawValue().length;
16144             
16145             if(selStart != len){
16146                 this.setRawValue(newValue);
16147                 this.selectText(selStart, newValue.length);
16148             }
16149         }
16150     },
16151
16152     // private
16153     onSelect : function(record, index){
16154         
16155         if(this.fireEvent('beforeselect', this, record, index) !== false){
16156         
16157             this.setFromData(index > -1 ? record.data : false);
16158             
16159             this.collapse();
16160             this.fireEvent('select', this, record, index);
16161         }
16162     },
16163
16164     /**
16165      * Returns the currently selected field value or empty string if no value is set.
16166      * @return {String} value The selected value
16167      */
16168     getValue : function()
16169     {
16170         if(Roo.isIOS && this.useNativeIOS){
16171             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16172         }
16173         
16174         if(this.multiple){
16175             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16176         }
16177         
16178         if(this.valueField){
16179             return typeof this.value != 'undefined' ? this.value : '';
16180         }else{
16181             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16182         }
16183     },
16184     
16185     getRawValue : function()
16186     {
16187         if(Roo.isIOS && this.useNativeIOS){
16188             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16189         }
16190         
16191         var v = this.inputEl().getValue();
16192         
16193         return v;
16194     },
16195
16196     /**
16197      * Clears any text/value currently set in the field
16198      */
16199     clearValue : function(){
16200         
16201         if(this.hiddenField){
16202             this.hiddenField.dom.value = '';
16203         }
16204         this.value = '';
16205         this.setRawValue('');
16206         this.lastSelectionText = '';
16207         this.lastData = false;
16208         
16209         var close = this.closeTriggerEl();
16210         
16211         if(close){
16212             close.hide();
16213         }
16214         
16215         this.validate();
16216         
16217     },
16218
16219     /**
16220      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16221      * will be displayed in the field.  If the value does not match the data value of an existing item,
16222      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16223      * Otherwise the field will be blank (although the value will still be set).
16224      * @param {String} value The value to match
16225      */
16226     setValue : function(v)
16227     {
16228         if(Roo.isIOS && this.useNativeIOS){
16229             this.setIOSValue(v);
16230             return;
16231         }
16232         
16233         if(this.multiple){
16234             this.syncValue();
16235             return;
16236         }
16237         
16238         var text = v;
16239         if(this.valueField){
16240             var r = this.findRecord(this.valueField, v);
16241             if(r){
16242                 text = r.data[this.displayField];
16243             }else if(this.valueNotFoundText !== undefined){
16244                 text = this.valueNotFoundText;
16245             }
16246         }
16247         this.lastSelectionText = text;
16248         if(this.hiddenField){
16249             this.hiddenField.dom.value = v;
16250         }
16251         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16252         this.value = v;
16253         
16254         var close = this.closeTriggerEl();
16255         
16256         if(close){
16257             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16258         }
16259         
16260         this.validate();
16261     },
16262     /**
16263      * @property {Object} the last set data for the element
16264      */
16265     
16266     lastData : false,
16267     /**
16268      * Sets the value of the field based on a object which is related to the record format for the store.
16269      * @param {Object} value the value to set as. or false on reset?
16270      */
16271     setFromData : function(o){
16272         
16273         if(this.multiple){
16274             this.addItem(o);
16275             return;
16276         }
16277             
16278         var dv = ''; // display value
16279         var vv = ''; // value value..
16280         this.lastData = o;
16281         if (this.displayField) {
16282             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16283         } else {
16284             // this is an error condition!!!
16285             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16286         }
16287         
16288         if(this.valueField){
16289             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16290         }
16291         
16292         var close = this.closeTriggerEl();
16293         
16294         if(close){
16295             if(dv.length || vv * 1 > 0){
16296                 close.show() ;
16297                 this.blockFocus=true;
16298             } else {
16299                 close.hide();
16300             }             
16301         }
16302         
16303         if(this.hiddenField){
16304             this.hiddenField.dom.value = vv;
16305             
16306             this.lastSelectionText = dv;
16307             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16308             this.value = vv;
16309             return;
16310         }
16311         // no hidden field.. - we store the value in 'value', but still display
16312         // display field!!!!
16313         this.lastSelectionText = dv;
16314         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16315         this.value = vv;
16316         
16317         
16318         
16319     },
16320     // private
16321     reset : function(){
16322         // overridden so that last data is reset..
16323         
16324         if(this.multiple){
16325             this.clearItem();
16326             return;
16327         }
16328         
16329         this.setValue(this.originalValue);
16330         //this.clearInvalid();
16331         this.lastData = false;
16332         if (this.view) {
16333             this.view.clearSelections();
16334         }
16335         
16336         this.validate();
16337     },
16338     // private
16339     findRecord : function(prop, value){
16340         var record;
16341         if(this.store.getCount() > 0){
16342             this.store.each(function(r){
16343                 if(r.data[prop] == value){
16344                     record = r;
16345                     return false;
16346                 }
16347                 return true;
16348             });
16349         }
16350         return record;
16351     },
16352     
16353     getName: function()
16354     {
16355         // returns hidden if it's set..
16356         if (!this.rendered) {return ''};
16357         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16358         
16359     },
16360     // private
16361     onViewMove : function(e, t){
16362         this.inKeyMode = false;
16363     },
16364
16365     // private
16366     onViewOver : function(e, t){
16367         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16368             return;
16369         }
16370         var item = this.view.findItemFromChild(t);
16371         
16372         if(item){
16373             var index = this.view.indexOf(item);
16374             this.select(index, false);
16375         }
16376     },
16377
16378     // private
16379     onViewClick : function(view, doFocus, el, e)
16380     {
16381         var index = this.view.getSelectedIndexes()[0];
16382         
16383         var r = this.store.getAt(index);
16384         
16385         if(this.tickable){
16386             
16387             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16388                 return;
16389             }
16390             
16391             var rm = false;
16392             var _this = this;
16393             
16394             Roo.each(this.tickItems, function(v,k){
16395                 
16396                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16397                     Roo.log(v);
16398                     _this.tickItems.splice(k, 1);
16399                     
16400                     if(typeof(e) == 'undefined' && view == false){
16401                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16402                     }
16403                     
16404                     rm = true;
16405                     return;
16406                 }
16407             });
16408             
16409             if(rm){
16410                 return;
16411             }
16412             
16413             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16414                 this.tickItems.push(r.data);
16415             }
16416             
16417             if(typeof(e) == 'undefined' && view == false){
16418                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16419             }
16420                     
16421             return;
16422         }
16423         
16424         if(r){
16425             this.onSelect(r, index);
16426         }
16427         if(doFocus !== false && !this.blockFocus){
16428             this.inputEl().focus();
16429         }
16430     },
16431
16432     // private
16433     restrictHeight : function(){
16434         //this.innerList.dom.style.height = '';
16435         //var inner = this.innerList.dom;
16436         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16437         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16438         //this.list.beginUpdate();
16439         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16440         this.list.alignTo(this.inputEl(), this.listAlign);
16441         this.list.alignTo(this.inputEl(), this.listAlign);
16442         //this.list.endUpdate();
16443     },
16444
16445     // private
16446     onEmptyResults : function(){
16447         
16448         if(this.tickable && this.editable){
16449             this.hasFocus = false;
16450             this.restrictHeight();
16451             return;
16452         }
16453         
16454         this.collapse();
16455     },
16456
16457     /**
16458      * Returns true if the dropdown list is expanded, else false.
16459      */
16460     isExpanded : function(){
16461         return this.list.isVisible();
16462     },
16463
16464     /**
16465      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16466      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16467      * @param {String} value The data value of the item to select
16468      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16469      * selected item if it is not currently in view (defaults to true)
16470      * @return {Boolean} True if the value matched an item in the list, else false
16471      */
16472     selectByValue : function(v, scrollIntoView){
16473         if(v !== undefined && v !== null){
16474             var r = this.findRecord(this.valueField || this.displayField, v);
16475             if(r){
16476                 this.select(this.store.indexOf(r), scrollIntoView);
16477                 return true;
16478             }
16479         }
16480         return false;
16481     },
16482
16483     /**
16484      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16485      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16486      * @param {Number} index The zero-based index of the list item to select
16487      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16488      * selected item if it is not currently in view (defaults to true)
16489      */
16490     select : function(index, scrollIntoView){
16491         this.selectedIndex = index;
16492         this.view.select(index);
16493         if(scrollIntoView !== false){
16494             var el = this.view.getNode(index);
16495             /*
16496              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16497              */
16498             if(el){
16499                 this.list.scrollChildIntoView(el, false);
16500             }
16501         }
16502     },
16503
16504     // private
16505     selectNext : function(){
16506         var ct = this.store.getCount();
16507         if(ct > 0){
16508             if(this.selectedIndex == -1){
16509                 this.select(0);
16510             }else if(this.selectedIndex < ct-1){
16511                 this.select(this.selectedIndex+1);
16512             }
16513         }
16514     },
16515
16516     // private
16517     selectPrev : function(){
16518         var ct = this.store.getCount();
16519         if(ct > 0){
16520             if(this.selectedIndex == -1){
16521                 this.select(0);
16522             }else if(this.selectedIndex != 0){
16523                 this.select(this.selectedIndex-1);
16524             }
16525         }
16526     },
16527
16528     // private
16529     onKeyUp : function(e){
16530         if(this.editable !== false && !e.isSpecialKey()){
16531             this.lastKey = e.getKey();
16532             this.dqTask.delay(this.queryDelay);
16533         }
16534     },
16535
16536     // private
16537     validateBlur : function(){
16538         return !this.list || !this.list.isVisible();   
16539     },
16540
16541     // private
16542     initQuery : function(){
16543         
16544         var v = this.getRawValue();
16545         
16546         if(this.tickable && this.editable){
16547             v = this.tickableInputEl().getValue();
16548         }
16549         
16550         this.doQuery(v);
16551     },
16552
16553     // private
16554     doForce : function(){
16555         if(this.inputEl().dom.value.length > 0){
16556             this.inputEl().dom.value =
16557                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16558              
16559         }
16560     },
16561
16562     /**
16563      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16564      * query allowing the query action to be canceled if needed.
16565      * @param {String} query The SQL query to execute
16566      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16567      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16568      * saved in the current store (defaults to false)
16569      */
16570     doQuery : function(q, forceAll){
16571         
16572         if(q === undefined || q === null){
16573             q = '';
16574         }
16575         var qe = {
16576             query: q,
16577             forceAll: forceAll,
16578             combo: this,
16579             cancel:false
16580         };
16581         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16582             return false;
16583         }
16584         q = qe.query;
16585         
16586         forceAll = qe.forceAll;
16587         if(forceAll === true || (q.length >= this.minChars)){
16588             
16589             this.hasQuery = true;
16590             
16591             if(this.lastQuery != q || this.alwaysQuery){
16592                 this.lastQuery = q;
16593                 if(this.mode == 'local'){
16594                     this.selectedIndex = -1;
16595                     if(forceAll){
16596                         this.store.clearFilter();
16597                     }else{
16598                         
16599                         if(this.specialFilter){
16600                             this.fireEvent('specialfilter', this);
16601                             this.onLoad();
16602                             return;
16603                         }
16604                         
16605                         this.store.filter(this.displayField, q);
16606                     }
16607                     
16608                     this.store.fireEvent("datachanged", this.store);
16609                     
16610                     this.onLoad();
16611                     
16612                     
16613                 }else{
16614                     
16615                     this.store.baseParams[this.queryParam] = q;
16616                     
16617                     var options = {params : this.getParams(q)};
16618                     
16619                     if(this.loadNext){
16620                         options.add = true;
16621                         options.params.start = this.page * this.pageSize;
16622                     }
16623                     
16624                     this.store.load(options);
16625                     
16626                     /*
16627                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16628                      *  we should expand the list on onLoad
16629                      *  so command out it
16630                      */
16631 //                    this.expand();
16632                 }
16633             }else{
16634                 this.selectedIndex = -1;
16635                 this.onLoad();   
16636             }
16637         }
16638         
16639         this.loadNext = false;
16640     },
16641     
16642     // private
16643     getParams : function(q){
16644         var p = {};
16645         //p[this.queryParam] = q;
16646         
16647         if(this.pageSize){
16648             p.start = 0;
16649             p.limit = this.pageSize;
16650         }
16651         return p;
16652     },
16653
16654     /**
16655      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16656      */
16657     collapse : function(){
16658         if(!this.isExpanded()){
16659             return;
16660         }
16661         
16662         this.list.hide();
16663         
16664         this.hasFocus = false;
16665         
16666         if(this.tickable){
16667             this.okBtn.hide();
16668             this.cancelBtn.hide();
16669             this.trigger.show();
16670             
16671             if(this.editable){
16672                 this.tickableInputEl().dom.value = '';
16673                 this.tickableInputEl().blur();
16674             }
16675             
16676         }
16677         
16678         Roo.get(document).un('mousedown', this.collapseIf, this);
16679         Roo.get(document).un('mousewheel', this.collapseIf, this);
16680         if (!this.editable) {
16681             Roo.get(document).un('keydown', this.listKeyPress, this);
16682         }
16683         this.fireEvent('collapse', this);
16684         
16685         this.validate();
16686     },
16687
16688     // private
16689     collapseIf : function(e){
16690         var in_combo  = e.within(this.el);
16691         var in_list =  e.within(this.list);
16692         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16693         
16694         if (in_combo || in_list || is_list) {
16695             //e.stopPropagation();
16696             return;
16697         }
16698         
16699         if(this.tickable){
16700             this.onTickableFooterButtonClick(e, false, false);
16701         }
16702
16703         this.collapse();
16704         
16705     },
16706
16707     /**
16708      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16709      */
16710     expand : function(){
16711        
16712         if(this.isExpanded() || !this.hasFocus){
16713             return;
16714         }
16715         
16716         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16717         this.list.setWidth(lw);
16718         
16719         Roo.log('expand');
16720         
16721         this.list.show();
16722         
16723         this.restrictHeight();
16724         
16725         if(this.tickable){
16726             
16727             this.tickItems = Roo.apply([], this.item);
16728             
16729             this.okBtn.show();
16730             this.cancelBtn.show();
16731             this.trigger.hide();
16732             
16733             if(this.editable){
16734                 this.tickableInputEl().focus();
16735             }
16736             
16737         }
16738         
16739         Roo.get(document).on('mousedown', this.collapseIf, this);
16740         Roo.get(document).on('mousewheel', this.collapseIf, this);
16741         if (!this.editable) {
16742             Roo.get(document).on('keydown', this.listKeyPress, this);
16743         }
16744         
16745         this.fireEvent('expand', this);
16746     },
16747
16748     // private
16749     // Implements the default empty TriggerField.onTriggerClick function
16750     onTriggerClick : function(e)
16751     {
16752         Roo.log('trigger click');
16753         
16754         if(this.disabled || !this.triggerList){
16755             return;
16756         }
16757         
16758         this.page = 0;
16759         this.loadNext = false;
16760         
16761         if(this.isExpanded()){
16762             this.collapse();
16763             if (!this.blockFocus) {
16764                 this.inputEl().focus();
16765             }
16766             
16767         }else {
16768             this.hasFocus = true;
16769             if(this.triggerAction == 'all') {
16770                 this.doQuery(this.allQuery, true);
16771             } else {
16772                 this.doQuery(this.getRawValue());
16773             }
16774             if (!this.blockFocus) {
16775                 this.inputEl().focus();
16776             }
16777         }
16778     },
16779     
16780     onTickableTriggerClick : function(e)
16781     {
16782         if(this.disabled){
16783             return;
16784         }
16785         
16786         this.page = 0;
16787         this.loadNext = false;
16788         this.hasFocus = true;
16789         
16790         if(this.triggerAction == 'all') {
16791             this.doQuery(this.allQuery, true);
16792         } else {
16793             this.doQuery(this.getRawValue());
16794         }
16795     },
16796     
16797     onSearchFieldClick : function(e)
16798     {
16799         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16800             this.onTickableFooterButtonClick(e, false, false);
16801             return;
16802         }
16803         
16804         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16805             return;
16806         }
16807         
16808         this.page = 0;
16809         this.loadNext = false;
16810         this.hasFocus = true;
16811         
16812         if(this.triggerAction == 'all') {
16813             this.doQuery(this.allQuery, true);
16814         } else {
16815             this.doQuery(this.getRawValue());
16816         }
16817     },
16818     
16819     listKeyPress : function(e)
16820     {
16821         //Roo.log('listkeypress');
16822         // scroll to first matching element based on key pres..
16823         if (e.isSpecialKey()) {
16824             return false;
16825         }
16826         var k = String.fromCharCode(e.getKey()).toUpperCase();
16827         //Roo.log(k);
16828         var match  = false;
16829         var csel = this.view.getSelectedNodes();
16830         var cselitem = false;
16831         if (csel.length) {
16832             var ix = this.view.indexOf(csel[0]);
16833             cselitem  = this.store.getAt(ix);
16834             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16835                 cselitem = false;
16836             }
16837             
16838         }
16839         
16840         this.store.each(function(v) { 
16841             if (cselitem) {
16842                 // start at existing selection.
16843                 if (cselitem.id == v.id) {
16844                     cselitem = false;
16845                 }
16846                 return true;
16847             }
16848                 
16849             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16850                 match = this.store.indexOf(v);
16851                 return false;
16852             }
16853             return true;
16854         }, this);
16855         
16856         if (match === false) {
16857             return true; // no more action?
16858         }
16859         // scroll to?
16860         this.view.select(match);
16861         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16862         sn.scrollIntoView(sn.dom.parentNode, false);
16863     },
16864     
16865     onViewScroll : function(e, t){
16866         
16867         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){
16868             return;
16869         }
16870         
16871         this.hasQuery = true;
16872         
16873         this.loading = this.list.select('.loading', true).first();
16874         
16875         if(this.loading === null){
16876             this.list.createChild({
16877                 tag: 'div',
16878                 cls: 'loading roo-select2-more-results roo-select2-active',
16879                 html: 'Loading more results...'
16880             });
16881             
16882             this.loading = this.list.select('.loading', true).first();
16883             
16884             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16885             
16886             this.loading.hide();
16887         }
16888         
16889         this.loading.show();
16890         
16891         var _combo = this;
16892         
16893         this.page++;
16894         this.loadNext = true;
16895         
16896         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16897         
16898         return;
16899     },
16900     
16901     addItem : function(o)
16902     {   
16903         var dv = ''; // display value
16904         
16905         if (this.displayField) {
16906             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16907         } else {
16908             // this is an error condition!!!
16909             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16910         }
16911         
16912         if(!dv.length){
16913             return;
16914         }
16915         
16916         var choice = this.choices.createChild({
16917             tag: 'li',
16918             cls: 'roo-select2-search-choice',
16919             cn: [
16920                 {
16921                     tag: 'div',
16922                     html: dv
16923                 },
16924                 {
16925                     tag: 'a',
16926                     href: '#',
16927                     cls: 'roo-select2-search-choice-close fa fa-times',
16928                     tabindex: '-1'
16929                 }
16930             ]
16931             
16932         }, this.searchField);
16933         
16934         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16935         
16936         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16937         
16938         this.item.push(o);
16939         
16940         this.lastData = o;
16941         
16942         this.syncValue();
16943         
16944         this.inputEl().dom.value = '';
16945         
16946         this.validate();
16947     },
16948     
16949     onRemoveItem : function(e, _self, o)
16950     {
16951         e.preventDefault();
16952         
16953         this.lastItem = Roo.apply([], this.item);
16954         
16955         var index = this.item.indexOf(o.data) * 1;
16956         
16957         if( index < 0){
16958             Roo.log('not this item?!');
16959             return;
16960         }
16961         
16962         this.item.splice(index, 1);
16963         o.item.remove();
16964         
16965         this.syncValue();
16966         
16967         this.fireEvent('remove', this, e);
16968         
16969         this.validate();
16970         
16971     },
16972     
16973     syncValue : function()
16974     {
16975         if(!this.item.length){
16976             this.clearValue();
16977             return;
16978         }
16979             
16980         var value = [];
16981         var _this = this;
16982         Roo.each(this.item, function(i){
16983             if(_this.valueField){
16984                 value.push(i[_this.valueField]);
16985                 return;
16986             }
16987
16988             value.push(i);
16989         });
16990
16991         this.value = value.join(',');
16992
16993         if(this.hiddenField){
16994             this.hiddenField.dom.value = this.value;
16995         }
16996         
16997         this.store.fireEvent("datachanged", this.store);
16998         
16999         this.validate();
17000     },
17001     
17002     clearItem : function()
17003     {
17004         if(!this.multiple){
17005             return;
17006         }
17007         
17008         this.item = [];
17009         
17010         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17011            c.remove();
17012         });
17013         
17014         this.syncValue();
17015         
17016         this.validate();
17017         
17018         if(this.tickable && !Roo.isTouch){
17019             this.view.refresh();
17020         }
17021     },
17022     
17023     inputEl: function ()
17024     {
17025         if(Roo.isIOS && this.useNativeIOS){
17026             return this.el.select('select.roo-ios-select', true).first();
17027         }
17028         
17029         if(Roo.isTouch && this.mobileTouchView){
17030             return this.el.select('input.form-control',true).first();
17031         }
17032         
17033         if(this.tickable){
17034             return this.searchField;
17035         }
17036         
17037         return this.el.select('input.form-control',true).first();
17038     },
17039     
17040     onTickableFooterButtonClick : function(e, btn, el)
17041     {
17042         e.preventDefault();
17043         
17044         this.lastItem = Roo.apply([], this.item);
17045         
17046         if(btn && btn.name == 'cancel'){
17047             this.tickItems = Roo.apply([], this.item);
17048             this.collapse();
17049             return;
17050         }
17051         
17052         this.clearItem();
17053         
17054         var _this = this;
17055         
17056         Roo.each(this.tickItems, function(o){
17057             _this.addItem(o);
17058         });
17059         
17060         this.collapse();
17061         
17062     },
17063     
17064     validate : function()
17065     {
17066         if(this.getVisibilityEl().hasClass('hidden')){
17067             return true;
17068         }
17069         
17070         var v = this.getRawValue();
17071         
17072         if(this.multiple){
17073             v = this.getValue();
17074         }
17075         
17076         if(this.disabled || this.allowBlank || v.length){
17077             this.markValid();
17078             return true;
17079         }
17080         
17081         this.markInvalid();
17082         return false;
17083     },
17084     
17085     tickableInputEl : function()
17086     {
17087         if(!this.tickable || !this.editable){
17088             return this.inputEl();
17089         }
17090         
17091         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17092     },
17093     
17094     
17095     getAutoCreateTouchView : function()
17096     {
17097         var id = Roo.id();
17098         
17099         var cfg = {
17100             cls: 'form-group' //input-group
17101         };
17102         
17103         var input =  {
17104             tag: 'input',
17105             id : id,
17106             type : this.inputType,
17107             cls : 'form-control x-combo-noedit',
17108             autocomplete: 'new-password',
17109             placeholder : this.placeholder || '',
17110             readonly : true
17111         };
17112         
17113         if (this.name) {
17114             input.name = this.name;
17115         }
17116         
17117         if (this.size) {
17118             input.cls += ' input-' + this.size;
17119         }
17120         
17121         if (this.disabled) {
17122             input.disabled = true;
17123         }
17124         
17125         var inputblock = {
17126             cls : 'roo-combobox-wrap',
17127             cn : [
17128                 input
17129             ]
17130         };
17131         
17132         if(this.before){
17133             inputblock.cls += ' input-group';
17134             
17135             inputblock.cn.unshift({
17136                 tag :'span',
17137                 cls : 'input-group-addon input-group-prepend input-group-text',
17138                 html : this.before
17139             });
17140         }
17141         
17142         if(this.removable && !this.multiple){
17143             inputblock.cls += ' roo-removable';
17144             
17145             inputblock.cn.push({
17146                 tag: 'button',
17147                 html : 'x',
17148                 cls : 'roo-combo-removable-btn close'
17149             });
17150         }
17151
17152         if(this.hasFeedback && !this.allowBlank){
17153             
17154             inputblock.cls += ' has-feedback';
17155             
17156             inputblock.cn.push({
17157                 tag: 'span',
17158                 cls: 'glyphicon form-control-feedback'
17159             });
17160             
17161         }
17162         
17163         if (this.after) {
17164             
17165             inputblock.cls += (this.before) ? '' : ' input-group';
17166             
17167             inputblock.cn.push({
17168                 tag :'span',
17169                 cls : 'input-group-addon input-group-append input-group-text',
17170                 html : this.after
17171             });
17172         }
17173
17174         
17175         var ibwrap = inputblock;
17176         
17177         if(this.multiple){
17178             ibwrap = {
17179                 tag: 'ul',
17180                 cls: 'roo-select2-choices',
17181                 cn:[
17182                     {
17183                         tag: 'li',
17184                         cls: 'roo-select2-search-field',
17185                         cn: [
17186
17187                             inputblock
17188                         ]
17189                     }
17190                 ]
17191             };
17192         
17193             
17194         }
17195         
17196         var combobox = {
17197             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17198             cn: [
17199                 {
17200                     tag: 'input',
17201                     type : 'hidden',
17202                     cls: 'form-hidden-field'
17203                 },
17204                 ibwrap
17205             ]
17206         };
17207         
17208         if(!this.multiple && this.showToggleBtn){
17209             
17210             var caret = {
17211                 cls: 'caret'
17212             };
17213             
17214             if (this.caret != false) {
17215                 caret = {
17216                      tag: 'i',
17217                      cls: 'fa fa-' + this.caret
17218                 };
17219                 
17220             }
17221             
17222             combobox.cn.push({
17223                 tag :'span',
17224                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17225                 cn : [
17226                     Roo.bootstrap.version == 3 ? caret : '',
17227                     {
17228                         tag: 'span',
17229                         cls: 'combobox-clear',
17230                         cn  : [
17231                             {
17232                                 tag : 'i',
17233                                 cls: 'icon-remove'
17234                             }
17235                         ]
17236                     }
17237                 ]
17238
17239             })
17240         }
17241         
17242         if(this.multiple){
17243             combobox.cls += ' roo-select2-container-multi';
17244         }
17245         
17246         var align = this.labelAlign || this.parentLabelAlign();
17247         
17248         if (align ==='left' && this.fieldLabel.length) {
17249
17250             cfg.cn = [
17251                 {
17252                    tag : 'i',
17253                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17254                    tooltip : 'This field is required'
17255                 },
17256                 {
17257                     tag: 'label',
17258                     cls : 'control-label col-form-label',
17259                     html : this.fieldLabel
17260
17261                 },
17262                 {
17263                     cls : 'roo-combobox-wrap ', 
17264                     cn: [
17265                         combobox
17266                     ]
17267                 }
17268             ];
17269             
17270             var labelCfg = cfg.cn[1];
17271             var contentCfg = cfg.cn[2];
17272             
17273
17274             if(this.indicatorpos == 'right'){
17275                 cfg.cn = [
17276                     {
17277                         tag: 'label',
17278                         'for' :  id,
17279                         cls : 'control-label col-form-label',
17280                         cn : [
17281                             {
17282                                 tag : 'span',
17283                                 html : this.fieldLabel
17284                             },
17285                             {
17286                                 tag : 'i',
17287                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17288                                 tooltip : 'This field is required'
17289                             }
17290                         ]
17291                     },
17292                     {
17293                         cls : "roo-combobox-wrap ",
17294                         cn: [
17295                             combobox
17296                         ]
17297                     }
17298
17299                 ];
17300                 
17301                 labelCfg = cfg.cn[0];
17302                 contentCfg = cfg.cn[1];
17303             }
17304             
17305            
17306             
17307             if(this.labelWidth > 12){
17308                 labelCfg.style = "width: " + this.labelWidth + 'px';
17309             }
17310            
17311             if(this.labelWidth < 13 && this.labelmd == 0){
17312                 this.labelmd = this.labelWidth;
17313             }
17314             
17315             if(this.labellg > 0){
17316                 labelCfg.cls += ' col-lg-' + this.labellg;
17317                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17318             }
17319             
17320             if(this.labelmd > 0){
17321                 labelCfg.cls += ' col-md-' + this.labelmd;
17322                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17323             }
17324             
17325             if(this.labelsm > 0){
17326                 labelCfg.cls += ' col-sm-' + this.labelsm;
17327                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17328             }
17329             
17330             if(this.labelxs > 0){
17331                 labelCfg.cls += ' col-xs-' + this.labelxs;
17332                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17333             }
17334                 
17335                 
17336         } else if ( this.fieldLabel.length) {
17337             cfg.cn = [
17338                 {
17339                    tag : 'i',
17340                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17341                    tooltip : 'This field is required'
17342                 },
17343                 {
17344                     tag: 'label',
17345                     cls : 'control-label',
17346                     html : this.fieldLabel
17347
17348                 },
17349                 {
17350                     cls : '', 
17351                     cn: [
17352                         combobox
17353                     ]
17354                 }
17355             ];
17356             
17357             if(this.indicatorpos == 'right'){
17358                 cfg.cn = [
17359                     {
17360                         tag: 'label',
17361                         cls : 'control-label',
17362                         html : this.fieldLabel,
17363                         cn : [
17364                             {
17365                                tag : 'i',
17366                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17367                                tooltip : 'This field is required'
17368                             }
17369                         ]
17370                     },
17371                     {
17372                         cls : '', 
17373                         cn: [
17374                             combobox
17375                         ]
17376                     }
17377                 ];
17378             }
17379         } else {
17380             cfg.cn = combobox;    
17381         }
17382         
17383         
17384         var settings = this;
17385         
17386         ['xs','sm','md','lg'].map(function(size){
17387             if (settings[size]) {
17388                 cfg.cls += ' col-' + size + '-' + settings[size];
17389             }
17390         });
17391         
17392         return cfg;
17393     },
17394     
17395     initTouchView : function()
17396     {
17397         this.renderTouchView();
17398         
17399         this.touchViewEl.on('scroll', function(){
17400             this.el.dom.scrollTop = 0;
17401         }, this);
17402         
17403         this.originalValue = this.getValue();
17404         
17405         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17406         
17407         this.inputEl().on("click", this.showTouchView, this);
17408         if (this.triggerEl) {
17409             this.triggerEl.on("click", this.showTouchView, this);
17410         }
17411         
17412         
17413         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17414         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17415         
17416         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17417         
17418         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17419         this.store.on('load', this.onTouchViewLoad, this);
17420         this.store.on('loadexception', this.onTouchViewLoadException, this);
17421         
17422         if(this.hiddenName){
17423             
17424             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17425             
17426             this.hiddenField.dom.value =
17427                 this.hiddenValue !== undefined ? this.hiddenValue :
17428                 this.value !== undefined ? this.value : '';
17429         
17430             this.el.dom.removeAttribute('name');
17431             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17432         }
17433         
17434         if(this.multiple){
17435             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17436             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17437         }
17438         
17439         if(this.removable && !this.multiple){
17440             var close = this.closeTriggerEl();
17441             if(close){
17442                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17443                 close.on('click', this.removeBtnClick, this, close);
17444             }
17445         }
17446         /*
17447          * fix the bug in Safari iOS8
17448          */
17449         this.inputEl().on("focus", function(e){
17450             document.activeElement.blur();
17451         }, this);
17452         
17453         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17454         
17455         return;
17456         
17457         
17458     },
17459     
17460     renderTouchView : function()
17461     {
17462         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17463         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17464         
17465         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17466         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17467         
17468         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17469         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17470         this.touchViewBodyEl.setStyle('overflow', 'auto');
17471         
17472         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17473         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17474         
17475         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17476         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17477         
17478     },
17479     
17480     showTouchView : function()
17481     {
17482         if(this.disabled){
17483             return;
17484         }
17485         
17486         this.touchViewHeaderEl.hide();
17487
17488         if(this.modalTitle.length){
17489             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17490             this.touchViewHeaderEl.show();
17491         }
17492
17493         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17494         this.touchViewEl.show();
17495
17496         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17497         
17498         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17499         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17500
17501         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17502
17503         if(this.modalTitle.length){
17504             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17505         }
17506         
17507         this.touchViewBodyEl.setHeight(bodyHeight);
17508
17509         if(this.animate){
17510             var _this = this;
17511             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17512         }else{
17513             this.touchViewEl.addClass(['in','show']);
17514         }
17515         
17516         if(this._touchViewMask){
17517             Roo.get(document.body).addClass("x-body-masked");
17518             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17519             this._touchViewMask.setStyle('z-index', 10000);
17520             this._touchViewMask.addClass('show');
17521         }
17522         
17523         this.doTouchViewQuery();
17524         
17525     },
17526     
17527     hideTouchView : function()
17528     {
17529         this.touchViewEl.removeClass(['in','show']);
17530
17531         if(this.animate){
17532             var _this = this;
17533             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17534         }else{
17535             this.touchViewEl.setStyle('display', 'none');
17536         }
17537         
17538         if(this._touchViewMask){
17539             this._touchViewMask.removeClass('show');
17540             Roo.get(document.body).removeClass("x-body-masked");
17541         }
17542     },
17543     
17544     setTouchViewValue : function()
17545     {
17546         if(this.multiple){
17547             this.clearItem();
17548         
17549             var _this = this;
17550
17551             Roo.each(this.tickItems, function(o){
17552                 this.addItem(o);
17553             }, this);
17554         }
17555         
17556         this.hideTouchView();
17557     },
17558     
17559     doTouchViewQuery : function()
17560     {
17561         var qe = {
17562             query: '',
17563             forceAll: true,
17564             combo: this,
17565             cancel:false
17566         };
17567         
17568         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17569             return false;
17570         }
17571         
17572         if(!this.alwaysQuery || this.mode == 'local'){
17573             this.onTouchViewLoad();
17574             return;
17575         }
17576         
17577         this.store.load();
17578     },
17579     
17580     onTouchViewBeforeLoad : function(combo,opts)
17581     {
17582         return;
17583     },
17584
17585     // private
17586     onTouchViewLoad : function()
17587     {
17588         if(this.store.getCount() < 1){
17589             this.onTouchViewEmptyResults();
17590             return;
17591         }
17592         
17593         this.clearTouchView();
17594         
17595         var rawValue = this.getRawValue();
17596         
17597         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17598         
17599         this.tickItems = [];
17600         
17601         this.store.data.each(function(d, rowIndex){
17602             var row = this.touchViewListGroup.createChild(template);
17603             
17604             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17605                 row.addClass(d.data.cls);
17606             }
17607             
17608             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17609                 var cfg = {
17610                     data : d.data,
17611                     html : d.data[this.displayField]
17612                 };
17613                 
17614                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17615                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17616                 }
17617             }
17618             row.removeClass('selected');
17619             if(!this.multiple && this.valueField &&
17620                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17621             {
17622                 // radio buttons..
17623                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17624                 row.addClass('selected');
17625             }
17626             
17627             if(this.multiple && this.valueField &&
17628                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17629             {
17630                 
17631                 // checkboxes...
17632                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17633                 this.tickItems.push(d.data);
17634             }
17635             
17636             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17637             
17638         }, this);
17639         
17640         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17641         
17642         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17643
17644         if(this.modalTitle.length){
17645             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17646         }
17647
17648         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17649         
17650         if(this.mobile_restrict_height && listHeight < bodyHeight){
17651             this.touchViewBodyEl.setHeight(listHeight);
17652         }
17653         
17654         var _this = this;
17655         
17656         if(firstChecked && listHeight > bodyHeight){
17657             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17658         }
17659         
17660     },
17661     
17662     onTouchViewLoadException : function()
17663     {
17664         this.hideTouchView();
17665     },
17666     
17667     onTouchViewEmptyResults : function()
17668     {
17669         this.clearTouchView();
17670         
17671         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17672         
17673         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17674         
17675     },
17676     
17677     clearTouchView : function()
17678     {
17679         this.touchViewListGroup.dom.innerHTML = '';
17680     },
17681     
17682     onTouchViewClick : function(e, el, o)
17683     {
17684         e.preventDefault();
17685         
17686         var row = o.row;
17687         var rowIndex = o.rowIndex;
17688         
17689         var r = this.store.getAt(rowIndex);
17690         
17691         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17692             
17693             if(!this.multiple){
17694                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17695                     c.dom.removeAttribute('checked');
17696                 }, this);
17697
17698                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17699
17700                 this.setFromData(r.data);
17701
17702                 var close = this.closeTriggerEl();
17703
17704                 if(close){
17705                     close.show();
17706                 }
17707
17708                 this.hideTouchView();
17709
17710                 this.fireEvent('select', this, r, rowIndex);
17711
17712                 return;
17713             }
17714
17715             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17716                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17717                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17718                 return;
17719             }
17720
17721             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17722             this.addItem(r.data);
17723             this.tickItems.push(r.data);
17724         }
17725     },
17726     
17727     getAutoCreateNativeIOS : function()
17728     {
17729         var cfg = {
17730             cls: 'form-group' //input-group,
17731         };
17732         
17733         var combobox =  {
17734             tag: 'select',
17735             cls : 'roo-ios-select'
17736         };
17737         
17738         if (this.name) {
17739             combobox.name = this.name;
17740         }
17741         
17742         if (this.disabled) {
17743             combobox.disabled = true;
17744         }
17745         
17746         var settings = this;
17747         
17748         ['xs','sm','md','lg'].map(function(size){
17749             if (settings[size]) {
17750                 cfg.cls += ' col-' + size + '-' + settings[size];
17751             }
17752         });
17753         
17754         cfg.cn = combobox;
17755         
17756         return cfg;
17757         
17758     },
17759     
17760     initIOSView : function()
17761     {
17762         this.store.on('load', this.onIOSViewLoad, this);
17763         
17764         return;
17765     },
17766     
17767     onIOSViewLoad : function()
17768     {
17769         if(this.store.getCount() < 1){
17770             return;
17771         }
17772         
17773         this.clearIOSView();
17774         
17775         if(this.allowBlank) {
17776             
17777             var default_text = '-- SELECT --';
17778             
17779             if(this.placeholder.length){
17780                 default_text = this.placeholder;
17781             }
17782             
17783             if(this.emptyTitle.length){
17784                 default_text += ' - ' + this.emptyTitle + ' -';
17785             }
17786             
17787             var opt = this.inputEl().createChild({
17788                 tag: 'option',
17789                 value : 0,
17790                 html : default_text
17791             });
17792             
17793             var o = {};
17794             o[this.valueField] = 0;
17795             o[this.displayField] = default_text;
17796             
17797             this.ios_options.push({
17798                 data : o,
17799                 el : opt
17800             });
17801             
17802         }
17803         
17804         this.store.data.each(function(d, rowIndex){
17805             
17806             var html = '';
17807             
17808             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17809                 html = d.data[this.displayField];
17810             }
17811             
17812             var value = '';
17813             
17814             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17815                 value = d.data[this.valueField];
17816             }
17817             
17818             var option = {
17819                 tag: 'option',
17820                 value : value,
17821                 html : html
17822             };
17823             
17824             if(this.value == d.data[this.valueField]){
17825                 option['selected'] = true;
17826             }
17827             
17828             var opt = this.inputEl().createChild(option);
17829             
17830             this.ios_options.push({
17831                 data : d.data,
17832                 el : opt
17833             });
17834             
17835         }, this);
17836         
17837         this.inputEl().on('change', function(){
17838            this.fireEvent('select', this);
17839         }, this);
17840         
17841     },
17842     
17843     clearIOSView: function()
17844     {
17845         this.inputEl().dom.innerHTML = '';
17846         
17847         this.ios_options = [];
17848     },
17849     
17850     setIOSValue: function(v)
17851     {
17852         this.value = v;
17853         
17854         if(!this.ios_options){
17855             return;
17856         }
17857         
17858         Roo.each(this.ios_options, function(opts){
17859            
17860            opts.el.dom.removeAttribute('selected');
17861            
17862            if(opts.data[this.valueField] != v){
17863                return;
17864            }
17865            
17866            opts.el.dom.setAttribute('selected', true);
17867            
17868         }, this);
17869     }
17870
17871     /** 
17872     * @cfg {Boolean} grow 
17873     * @hide 
17874     */
17875     /** 
17876     * @cfg {Number} growMin 
17877     * @hide 
17878     */
17879     /** 
17880     * @cfg {Number} growMax 
17881     * @hide 
17882     */
17883     /**
17884      * @hide
17885      * @method autoSize
17886      */
17887 });
17888
17889 Roo.apply(Roo.bootstrap.ComboBox,  {
17890     
17891     header : {
17892         tag: 'div',
17893         cls: 'modal-header',
17894         cn: [
17895             {
17896                 tag: 'h4',
17897                 cls: 'modal-title'
17898             }
17899         ]
17900     },
17901     
17902     body : {
17903         tag: 'div',
17904         cls: 'modal-body',
17905         cn: [
17906             {
17907                 tag: 'ul',
17908                 cls: 'list-group'
17909             }
17910         ]
17911     },
17912     
17913     listItemRadio : {
17914         tag: 'li',
17915         cls: 'list-group-item',
17916         cn: [
17917             {
17918                 tag: 'span',
17919                 cls: 'roo-combobox-list-group-item-value'
17920             },
17921             {
17922                 tag: 'div',
17923                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17924                 cn: [
17925                     {
17926                         tag: 'input',
17927                         type: 'radio'
17928                     },
17929                     {
17930                         tag: 'label'
17931                     }
17932                 ]
17933             }
17934         ]
17935     },
17936     
17937     listItemCheckbox : {
17938         tag: 'li',
17939         cls: 'list-group-item',
17940         cn: [
17941             {
17942                 tag: 'span',
17943                 cls: 'roo-combobox-list-group-item-value'
17944             },
17945             {
17946                 tag: 'div',
17947                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17948                 cn: [
17949                     {
17950                         tag: 'input',
17951                         type: 'checkbox'
17952                     },
17953                     {
17954                         tag: 'label'
17955                     }
17956                 ]
17957             }
17958         ]
17959     },
17960     
17961     emptyResult : {
17962         tag: 'div',
17963         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17964     },
17965     
17966     footer : {
17967         tag: 'div',
17968         cls: 'modal-footer',
17969         cn: [
17970             {
17971                 tag: 'div',
17972                 cls: 'row',
17973                 cn: [
17974                     {
17975                         tag: 'div',
17976                         cls: 'col-xs-6 text-left',
17977                         cn: {
17978                             tag: 'button',
17979                             cls: 'btn btn-danger roo-touch-view-cancel',
17980                             html: 'Cancel'
17981                         }
17982                     },
17983                     {
17984                         tag: 'div',
17985                         cls: 'col-xs-6 text-right',
17986                         cn: {
17987                             tag: 'button',
17988                             cls: 'btn btn-success roo-touch-view-ok',
17989                             html: 'OK'
17990                         }
17991                     }
17992                 ]
17993             }
17994         ]
17995         
17996     }
17997 });
17998
17999 Roo.apply(Roo.bootstrap.ComboBox,  {
18000     
18001     touchViewTemplate : {
18002         tag: 'div',
18003         cls: 'modal fade roo-combobox-touch-view',
18004         cn: [
18005             {
18006                 tag: 'div',
18007                 cls: 'modal-dialog',
18008                 style : 'position:fixed', // we have to fix position....
18009                 cn: [
18010                     {
18011                         tag: 'div',
18012                         cls: 'modal-content',
18013                         cn: [
18014                             Roo.bootstrap.ComboBox.header,
18015                             Roo.bootstrap.ComboBox.body,
18016                             Roo.bootstrap.ComboBox.footer
18017                         ]
18018                     }
18019                 ]
18020             }
18021         ]
18022     }
18023 });/*
18024  * Based on:
18025  * Ext JS Library 1.1.1
18026  * Copyright(c) 2006-2007, Ext JS, LLC.
18027  *
18028  * Originally Released Under LGPL - original licence link has changed is not relivant.
18029  *
18030  * Fork - LGPL
18031  * <script type="text/javascript">
18032  */
18033
18034 /**
18035  * @class Roo.View
18036  * @extends Roo.util.Observable
18037  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18038  * This class also supports single and multi selection modes. <br>
18039  * Create a data model bound view:
18040  <pre><code>
18041  var store = new Roo.data.Store(...);
18042
18043  var view = new Roo.View({
18044     el : "my-element",
18045     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18046  
18047     singleSelect: true,
18048     selectedClass: "ydataview-selected",
18049     store: store
18050  });
18051
18052  // listen for node click?
18053  view.on("click", function(vw, index, node, e){
18054  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18055  });
18056
18057  // load XML data
18058  dataModel.load("foobar.xml");
18059  </code></pre>
18060  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18061  * <br><br>
18062  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18063  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18064  * 
18065  * Note: old style constructor is still suported (container, template, config)
18066  * 
18067  * @constructor
18068  * Create a new View
18069  * @param {Object} config The config object
18070  * 
18071  */
18072 Roo.View = function(config, depreciated_tpl, depreciated_config){
18073     
18074     this.parent = false;
18075     
18076     if (typeof(depreciated_tpl) == 'undefined') {
18077         // new way.. - universal constructor.
18078         Roo.apply(this, config);
18079         this.el  = Roo.get(this.el);
18080     } else {
18081         // old format..
18082         this.el  = Roo.get(config);
18083         this.tpl = depreciated_tpl;
18084         Roo.apply(this, depreciated_config);
18085     }
18086     this.wrapEl  = this.el.wrap().wrap();
18087     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18088     
18089     
18090     if(typeof(this.tpl) == "string"){
18091         this.tpl = new Roo.Template(this.tpl);
18092     } else {
18093         // support xtype ctors..
18094         this.tpl = new Roo.factory(this.tpl, Roo);
18095     }
18096     
18097     
18098     this.tpl.compile();
18099     
18100     /** @private */
18101     this.addEvents({
18102         /**
18103          * @event beforeclick
18104          * Fires before a click is processed. Returns false to cancel the default action.
18105          * @param {Roo.View} this
18106          * @param {Number} index The index of the target node
18107          * @param {HTMLElement} node The target node
18108          * @param {Roo.EventObject} e The raw event object
18109          */
18110             "beforeclick" : true,
18111         /**
18112          * @event click
18113          * Fires when a template node is clicked.
18114          * @param {Roo.View} this
18115          * @param {Number} index The index of the target node
18116          * @param {HTMLElement} node The target node
18117          * @param {Roo.EventObject} e The raw event object
18118          */
18119             "click" : true,
18120         /**
18121          * @event dblclick
18122          * Fires when a template node is double clicked.
18123          * @param {Roo.View} this
18124          * @param {Number} index The index of the target node
18125          * @param {HTMLElement} node The target node
18126          * @param {Roo.EventObject} e The raw event object
18127          */
18128             "dblclick" : true,
18129         /**
18130          * @event contextmenu
18131          * Fires when a template node is right clicked.
18132          * @param {Roo.View} this
18133          * @param {Number} index The index of the target node
18134          * @param {HTMLElement} node The target node
18135          * @param {Roo.EventObject} e The raw event object
18136          */
18137             "contextmenu" : true,
18138         /**
18139          * @event selectionchange
18140          * Fires when the selected nodes change.
18141          * @param {Roo.View} this
18142          * @param {Array} selections Array of the selected nodes
18143          */
18144             "selectionchange" : true,
18145     
18146         /**
18147          * @event beforeselect
18148          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18149          * @param {Roo.View} this
18150          * @param {HTMLElement} node The node to be selected
18151          * @param {Array} selections Array of currently selected nodes
18152          */
18153             "beforeselect" : true,
18154         /**
18155          * @event preparedata
18156          * Fires on every row to render, to allow you to change the data.
18157          * @param {Roo.View} this
18158          * @param {Object} data to be rendered (change this)
18159          */
18160           "preparedata" : true
18161           
18162           
18163         });
18164
18165
18166
18167     this.el.on({
18168         "click": this.onClick,
18169         "dblclick": this.onDblClick,
18170         "contextmenu": this.onContextMenu,
18171         scope:this
18172     });
18173
18174     this.selections = [];
18175     this.nodes = [];
18176     this.cmp = new Roo.CompositeElementLite([]);
18177     if(this.store){
18178         this.store = Roo.factory(this.store, Roo.data);
18179         this.setStore(this.store, true);
18180     }
18181     
18182     if ( this.footer && this.footer.xtype) {
18183            
18184          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18185         
18186         this.footer.dataSource = this.store;
18187         this.footer.container = fctr;
18188         this.footer = Roo.factory(this.footer, Roo);
18189         fctr.insertFirst(this.el);
18190         
18191         // this is a bit insane - as the paging toolbar seems to detach the el..
18192 //        dom.parentNode.parentNode.parentNode
18193          // they get detached?
18194     }
18195     
18196     
18197     Roo.View.superclass.constructor.call(this);
18198     
18199     
18200 };
18201
18202 Roo.extend(Roo.View, Roo.util.Observable, {
18203     
18204      /**
18205      * @cfg {Roo.data.Store} store Data store to load data from.
18206      */
18207     store : false,
18208     
18209     /**
18210      * @cfg {String|Roo.Element} el The container element.
18211      */
18212     el : '',
18213     
18214     /**
18215      * @cfg {String|Roo.Template} tpl The template used by this View 
18216      */
18217     tpl : false,
18218     /**
18219      * @cfg {String} dataName the named area of the template to use as the data area
18220      *                          Works with domtemplates roo-name="name"
18221      */
18222     dataName: false,
18223     /**
18224      * @cfg {String} selectedClass The css class to add to selected nodes
18225      */
18226     selectedClass : "x-view-selected",
18227      /**
18228      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18229      */
18230     emptyText : "",
18231     
18232     /**
18233      * @cfg {String} text to display on mask (default Loading)
18234      */
18235     mask : false,
18236     /**
18237      * @cfg {Boolean} multiSelect Allow multiple selection
18238      */
18239     multiSelect : false,
18240     /**
18241      * @cfg {Boolean} singleSelect Allow single selection
18242      */
18243     singleSelect:  false,
18244     
18245     /**
18246      * @cfg {Boolean} toggleSelect - selecting 
18247      */
18248     toggleSelect : false,
18249     
18250     /**
18251      * @cfg {Boolean} tickable - selecting 
18252      */
18253     tickable : false,
18254     
18255     /**
18256      * Returns the element this view is bound to.
18257      * @return {Roo.Element}
18258      */
18259     getEl : function(){
18260         return this.wrapEl;
18261     },
18262     
18263     
18264
18265     /**
18266      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18267      */
18268     refresh : function(){
18269         //Roo.log('refresh');
18270         var t = this.tpl;
18271         
18272         // if we are using something like 'domtemplate', then
18273         // the what gets used is:
18274         // t.applySubtemplate(NAME, data, wrapping data..)
18275         // the outer template then get' applied with
18276         //     the store 'extra data'
18277         // and the body get's added to the
18278         //      roo-name="data" node?
18279         //      <span class='roo-tpl-{name}'></span> ?????
18280         
18281         
18282         
18283         this.clearSelections();
18284         this.el.update("");
18285         var html = [];
18286         var records = this.store.getRange();
18287         if(records.length < 1) {
18288             
18289             // is this valid??  = should it render a template??
18290             
18291             this.el.update(this.emptyText);
18292             return;
18293         }
18294         var el = this.el;
18295         if (this.dataName) {
18296             this.el.update(t.apply(this.store.meta)); //????
18297             el = this.el.child('.roo-tpl-' + this.dataName);
18298         }
18299         
18300         for(var i = 0, len = records.length; i < len; i++){
18301             var data = this.prepareData(records[i].data, i, records[i]);
18302             this.fireEvent("preparedata", this, data, i, records[i]);
18303             
18304             var d = Roo.apply({}, data);
18305             
18306             if(this.tickable){
18307                 Roo.apply(d, {'roo-id' : Roo.id()});
18308                 
18309                 var _this = this;
18310             
18311                 Roo.each(this.parent.item, function(item){
18312                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18313                         return;
18314                     }
18315                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18316                 });
18317             }
18318             
18319             html[html.length] = Roo.util.Format.trim(
18320                 this.dataName ?
18321                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18322                     t.apply(d)
18323             );
18324         }
18325         
18326         
18327         
18328         el.update(html.join(""));
18329         this.nodes = el.dom.childNodes;
18330         this.updateIndexes(0);
18331     },
18332     
18333
18334     /**
18335      * Function to override to reformat the data that is sent to
18336      * the template for each node.
18337      * DEPRICATED - use the preparedata event handler.
18338      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18339      * a JSON object for an UpdateManager bound view).
18340      */
18341     prepareData : function(data, index, record)
18342     {
18343         this.fireEvent("preparedata", this, data, index, record);
18344         return data;
18345     },
18346
18347     onUpdate : function(ds, record){
18348         // Roo.log('on update');   
18349         this.clearSelections();
18350         var index = this.store.indexOf(record);
18351         var n = this.nodes[index];
18352         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18353         n.parentNode.removeChild(n);
18354         this.updateIndexes(index, index);
18355     },
18356
18357     
18358     
18359 // --------- FIXME     
18360     onAdd : function(ds, records, index)
18361     {
18362         //Roo.log(['on Add', ds, records, index] );        
18363         this.clearSelections();
18364         if(this.nodes.length == 0){
18365             this.refresh();
18366             return;
18367         }
18368         var n = this.nodes[index];
18369         for(var i = 0, len = records.length; i < len; i++){
18370             var d = this.prepareData(records[i].data, i, records[i]);
18371             if(n){
18372                 this.tpl.insertBefore(n, d);
18373             }else{
18374                 
18375                 this.tpl.append(this.el, d);
18376             }
18377         }
18378         this.updateIndexes(index);
18379     },
18380
18381     onRemove : function(ds, record, index){
18382        // Roo.log('onRemove');
18383         this.clearSelections();
18384         var el = this.dataName  ?
18385             this.el.child('.roo-tpl-' + this.dataName) :
18386             this.el; 
18387         
18388         el.dom.removeChild(this.nodes[index]);
18389         this.updateIndexes(index);
18390     },
18391
18392     /**
18393      * Refresh an individual node.
18394      * @param {Number} index
18395      */
18396     refreshNode : function(index){
18397         this.onUpdate(this.store, this.store.getAt(index));
18398     },
18399
18400     updateIndexes : function(startIndex, endIndex){
18401         var ns = this.nodes;
18402         startIndex = startIndex || 0;
18403         endIndex = endIndex || ns.length - 1;
18404         for(var i = startIndex; i <= endIndex; i++){
18405             ns[i].nodeIndex = i;
18406         }
18407     },
18408
18409     /**
18410      * Changes the data store this view uses and refresh the view.
18411      * @param {Store} store
18412      */
18413     setStore : function(store, initial){
18414         if(!initial && this.store){
18415             this.store.un("datachanged", this.refresh);
18416             this.store.un("add", this.onAdd);
18417             this.store.un("remove", this.onRemove);
18418             this.store.un("update", this.onUpdate);
18419             this.store.un("clear", this.refresh);
18420             this.store.un("beforeload", this.onBeforeLoad);
18421             this.store.un("load", this.onLoad);
18422             this.store.un("loadexception", this.onLoad);
18423         }
18424         if(store){
18425           
18426             store.on("datachanged", this.refresh, this);
18427             store.on("add", this.onAdd, this);
18428             store.on("remove", this.onRemove, this);
18429             store.on("update", this.onUpdate, this);
18430             store.on("clear", this.refresh, this);
18431             store.on("beforeload", this.onBeforeLoad, this);
18432             store.on("load", this.onLoad, this);
18433             store.on("loadexception", this.onLoad, this);
18434         }
18435         
18436         if(store){
18437             this.refresh();
18438         }
18439     },
18440     /**
18441      * onbeforeLoad - masks the loading area.
18442      *
18443      */
18444     onBeforeLoad : function(store,opts)
18445     {
18446          //Roo.log('onBeforeLoad');   
18447         if (!opts.add) {
18448             this.el.update("");
18449         }
18450         this.el.mask(this.mask ? this.mask : "Loading" ); 
18451     },
18452     onLoad : function ()
18453     {
18454         this.el.unmask();
18455     },
18456     
18457
18458     /**
18459      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18460      * @param {HTMLElement} node
18461      * @return {HTMLElement} The template node
18462      */
18463     findItemFromChild : function(node){
18464         var el = this.dataName  ?
18465             this.el.child('.roo-tpl-' + this.dataName,true) :
18466             this.el.dom; 
18467         
18468         if(!node || node.parentNode == el){
18469                     return node;
18470             }
18471             var p = node.parentNode;
18472             while(p && p != el){
18473             if(p.parentNode == el){
18474                 return p;
18475             }
18476             p = p.parentNode;
18477         }
18478             return null;
18479     },
18480
18481     /** @ignore */
18482     onClick : function(e){
18483         var item = this.findItemFromChild(e.getTarget());
18484         if(item){
18485             var index = this.indexOf(item);
18486             if(this.onItemClick(item, index, e) !== false){
18487                 this.fireEvent("click", this, index, item, e);
18488             }
18489         }else{
18490             this.clearSelections();
18491         }
18492     },
18493
18494     /** @ignore */
18495     onContextMenu : function(e){
18496         var item = this.findItemFromChild(e.getTarget());
18497         if(item){
18498             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18499         }
18500     },
18501
18502     /** @ignore */
18503     onDblClick : function(e){
18504         var item = this.findItemFromChild(e.getTarget());
18505         if(item){
18506             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18507         }
18508     },
18509
18510     onItemClick : function(item, index, e)
18511     {
18512         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18513             return false;
18514         }
18515         if (this.toggleSelect) {
18516             var m = this.isSelected(item) ? 'unselect' : 'select';
18517             //Roo.log(m);
18518             var _t = this;
18519             _t[m](item, true, false);
18520             return true;
18521         }
18522         if(this.multiSelect || this.singleSelect){
18523             if(this.multiSelect && e.shiftKey && this.lastSelection){
18524                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18525             }else{
18526                 this.select(item, this.multiSelect && e.ctrlKey);
18527                 this.lastSelection = item;
18528             }
18529             
18530             if(!this.tickable){
18531                 e.preventDefault();
18532             }
18533             
18534         }
18535         return true;
18536     },
18537
18538     /**
18539      * Get the number of selected nodes.
18540      * @return {Number}
18541      */
18542     getSelectionCount : function(){
18543         return this.selections.length;
18544     },
18545
18546     /**
18547      * Get the currently selected nodes.
18548      * @return {Array} An array of HTMLElements
18549      */
18550     getSelectedNodes : function(){
18551         return this.selections;
18552     },
18553
18554     /**
18555      * Get the indexes of the selected nodes.
18556      * @return {Array}
18557      */
18558     getSelectedIndexes : function(){
18559         var indexes = [], s = this.selections;
18560         for(var i = 0, len = s.length; i < len; i++){
18561             indexes.push(s[i].nodeIndex);
18562         }
18563         return indexes;
18564     },
18565
18566     /**
18567      * Clear all selections
18568      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18569      */
18570     clearSelections : function(suppressEvent){
18571         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18572             this.cmp.elements = this.selections;
18573             this.cmp.removeClass(this.selectedClass);
18574             this.selections = [];
18575             if(!suppressEvent){
18576                 this.fireEvent("selectionchange", this, this.selections);
18577             }
18578         }
18579     },
18580
18581     /**
18582      * Returns true if the passed node is selected
18583      * @param {HTMLElement/Number} node The node or node index
18584      * @return {Boolean}
18585      */
18586     isSelected : function(node){
18587         var s = this.selections;
18588         if(s.length < 1){
18589             return false;
18590         }
18591         node = this.getNode(node);
18592         return s.indexOf(node) !== -1;
18593     },
18594
18595     /**
18596      * Selects nodes.
18597      * @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
18598      * @param {Boolean} keepExisting (optional) true to keep existing selections
18599      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18600      */
18601     select : function(nodeInfo, keepExisting, suppressEvent){
18602         if(nodeInfo instanceof Array){
18603             if(!keepExisting){
18604                 this.clearSelections(true);
18605             }
18606             for(var i = 0, len = nodeInfo.length; i < len; i++){
18607                 this.select(nodeInfo[i], true, true);
18608             }
18609             return;
18610         } 
18611         var node = this.getNode(nodeInfo);
18612         if(!node || this.isSelected(node)){
18613             return; // already selected.
18614         }
18615         if(!keepExisting){
18616             this.clearSelections(true);
18617         }
18618         
18619         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18620             Roo.fly(node).addClass(this.selectedClass);
18621             this.selections.push(node);
18622             if(!suppressEvent){
18623                 this.fireEvent("selectionchange", this, this.selections);
18624             }
18625         }
18626         
18627         
18628     },
18629       /**
18630      * Unselects nodes.
18631      * @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
18632      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18633      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18634      */
18635     unselect : function(nodeInfo, keepExisting, suppressEvent)
18636     {
18637         if(nodeInfo instanceof Array){
18638             Roo.each(this.selections, function(s) {
18639                 this.unselect(s, nodeInfo);
18640             }, this);
18641             return;
18642         }
18643         var node = this.getNode(nodeInfo);
18644         if(!node || !this.isSelected(node)){
18645             //Roo.log("not selected");
18646             return; // not selected.
18647         }
18648         // fireevent???
18649         var ns = [];
18650         Roo.each(this.selections, function(s) {
18651             if (s == node ) {
18652                 Roo.fly(node).removeClass(this.selectedClass);
18653
18654                 return;
18655             }
18656             ns.push(s);
18657         },this);
18658         
18659         this.selections= ns;
18660         this.fireEvent("selectionchange", this, this.selections);
18661     },
18662
18663     /**
18664      * Gets a template node.
18665      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18666      * @return {HTMLElement} The node or null if it wasn't found
18667      */
18668     getNode : function(nodeInfo){
18669         if(typeof nodeInfo == "string"){
18670             return document.getElementById(nodeInfo);
18671         }else if(typeof nodeInfo == "number"){
18672             return this.nodes[nodeInfo];
18673         }
18674         return nodeInfo;
18675     },
18676
18677     /**
18678      * Gets a range template nodes.
18679      * @param {Number} startIndex
18680      * @param {Number} endIndex
18681      * @return {Array} An array of nodes
18682      */
18683     getNodes : function(start, end){
18684         var ns = this.nodes;
18685         start = start || 0;
18686         end = typeof end == "undefined" ? ns.length - 1 : end;
18687         var nodes = [];
18688         if(start <= end){
18689             for(var i = start; i <= end; i++){
18690                 nodes.push(ns[i]);
18691             }
18692         } else{
18693             for(var i = start; i >= end; i--){
18694                 nodes.push(ns[i]);
18695             }
18696         }
18697         return nodes;
18698     },
18699
18700     /**
18701      * Finds the index of the passed node
18702      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18703      * @return {Number} The index of the node or -1
18704      */
18705     indexOf : function(node){
18706         node = this.getNode(node);
18707         if(typeof node.nodeIndex == "number"){
18708             return node.nodeIndex;
18709         }
18710         var ns = this.nodes;
18711         for(var i = 0, len = ns.length; i < len; i++){
18712             if(ns[i] == node){
18713                 return i;
18714             }
18715         }
18716         return -1;
18717     }
18718 });
18719 /*
18720  * - LGPL
18721  *
18722  * based on jquery fullcalendar
18723  * 
18724  */
18725
18726 Roo.bootstrap = Roo.bootstrap || {};
18727 /**
18728  * @class Roo.bootstrap.Calendar
18729  * @extends Roo.bootstrap.Component
18730  * Bootstrap Calendar class
18731  * @cfg {Boolean} loadMask (true|false) default false
18732  * @cfg {Object} header generate the user specific header of the calendar, default false
18733
18734  * @constructor
18735  * Create a new Container
18736  * @param {Object} config The config object
18737  */
18738
18739
18740
18741 Roo.bootstrap.Calendar = function(config){
18742     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18743      this.addEvents({
18744         /**
18745              * @event select
18746              * Fires when a date is selected
18747              * @param {DatePicker} this
18748              * @param {Date} date The selected date
18749              */
18750         'select': true,
18751         /**
18752              * @event monthchange
18753              * Fires when the displayed month changes 
18754              * @param {DatePicker} this
18755              * @param {Date} date The selected month
18756              */
18757         'monthchange': true,
18758         /**
18759              * @event evententer
18760              * Fires when mouse over an event
18761              * @param {Calendar} this
18762              * @param {event} Event
18763              */
18764         'evententer': true,
18765         /**
18766              * @event eventleave
18767              * Fires when the mouse leaves an
18768              * @param {Calendar} this
18769              * @param {event}
18770              */
18771         'eventleave': true,
18772         /**
18773              * @event eventclick
18774              * Fires when the mouse click an
18775              * @param {Calendar} this
18776              * @param {event}
18777              */
18778         'eventclick': true
18779         
18780     });
18781
18782 };
18783
18784 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18785     
18786      /**
18787      * @cfg {Number} startDay
18788      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18789      */
18790     startDay : 0,
18791     
18792     loadMask : false,
18793     
18794     header : false,
18795       
18796     getAutoCreate : function(){
18797         
18798         
18799         var fc_button = function(name, corner, style, content ) {
18800             return Roo.apply({},{
18801                 tag : 'span',
18802                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18803                          (corner.length ?
18804                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18805                             ''
18806                         ),
18807                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18808                 unselectable: 'on'
18809             });
18810         };
18811         
18812         var header = {};
18813         
18814         if(!this.header){
18815             header = {
18816                 tag : 'table',
18817                 cls : 'fc-header',
18818                 style : 'width:100%',
18819                 cn : [
18820                     {
18821                         tag: 'tr',
18822                         cn : [
18823                             {
18824                                 tag : 'td',
18825                                 cls : 'fc-header-left',
18826                                 cn : [
18827                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18828                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18829                                     { tag: 'span', cls: 'fc-header-space' },
18830                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18831
18832
18833                                 ]
18834                             },
18835
18836                             {
18837                                 tag : 'td',
18838                                 cls : 'fc-header-center',
18839                                 cn : [
18840                                     {
18841                                         tag: 'span',
18842                                         cls: 'fc-header-title',
18843                                         cn : {
18844                                             tag: 'H2',
18845                                             html : 'month / year'
18846                                         }
18847                                     }
18848
18849                                 ]
18850                             },
18851                             {
18852                                 tag : 'td',
18853                                 cls : 'fc-header-right',
18854                                 cn : [
18855                               /*      fc_button('month', 'left', '', 'month' ),
18856                                     fc_button('week', '', '', 'week' ),
18857                                     fc_button('day', 'right', '', 'day' )
18858                                 */    
18859
18860                                 ]
18861                             }
18862
18863                         ]
18864                     }
18865                 ]
18866             };
18867         }
18868         
18869         header = this.header;
18870         
18871        
18872         var cal_heads = function() {
18873             var ret = [];
18874             // fixme - handle this.
18875             
18876             for (var i =0; i < Date.dayNames.length; i++) {
18877                 var d = Date.dayNames[i];
18878                 ret.push({
18879                     tag: 'th',
18880                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18881                     html : d.substring(0,3)
18882                 });
18883                 
18884             }
18885             ret[0].cls += ' fc-first';
18886             ret[6].cls += ' fc-last';
18887             return ret;
18888         };
18889         var cal_cell = function(n) {
18890             return  {
18891                 tag: 'td',
18892                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18893                 cn : [
18894                     {
18895                         cn : [
18896                             {
18897                                 cls: 'fc-day-number',
18898                                 html: 'D'
18899                             },
18900                             {
18901                                 cls: 'fc-day-content',
18902                              
18903                                 cn : [
18904                                      {
18905                                         style: 'position: relative;' // height: 17px;
18906                                     }
18907                                 ]
18908                             }
18909                             
18910                             
18911                         ]
18912                     }
18913                 ]
18914                 
18915             }
18916         };
18917         var cal_rows = function() {
18918             
18919             var ret = [];
18920             for (var r = 0; r < 6; r++) {
18921                 var row= {
18922                     tag : 'tr',
18923                     cls : 'fc-week',
18924                     cn : []
18925                 };
18926                 
18927                 for (var i =0; i < Date.dayNames.length; i++) {
18928                     var d = Date.dayNames[i];
18929                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18930
18931                 }
18932                 row.cn[0].cls+=' fc-first';
18933                 row.cn[0].cn[0].style = 'min-height:90px';
18934                 row.cn[6].cls+=' fc-last';
18935                 ret.push(row);
18936                 
18937             }
18938             ret[0].cls += ' fc-first';
18939             ret[4].cls += ' fc-prev-last';
18940             ret[5].cls += ' fc-last';
18941             return ret;
18942             
18943         };
18944         
18945         var cal_table = {
18946             tag: 'table',
18947             cls: 'fc-border-separate',
18948             style : 'width:100%',
18949             cellspacing  : 0,
18950             cn : [
18951                 { 
18952                     tag: 'thead',
18953                     cn : [
18954                         { 
18955                             tag: 'tr',
18956                             cls : 'fc-first fc-last',
18957                             cn : cal_heads()
18958                         }
18959                     ]
18960                 },
18961                 { 
18962                     tag: 'tbody',
18963                     cn : cal_rows()
18964                 }
18965                   
18966             ]
18967         };
18968          
18969          var cfg = {
18970             cls : 'fc fc-ltr',
18971             cn : [
18972                 header,
18973                 {
18974                     cls : 'fc-content',
18975                     style : "position: relative;",
18976                     cn : [
18977                         {
18978                             cls : 'fc-view fc-view-month fc-grid',
18979                             style : 'position: relative',
18980                             unselectable : 'on',
18981                             cn : [
18982                                 {
18983                                     cls : 'fc-event-container',
18984                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18985                                 },
18986                                 cal_table
18987                             ]
18988                         }
18989                     ]
18990     
18991                 }
18992            ] 
18993             
18994         };
18995         
18996          
18997         
18998         return cfg;
18999     },
19000     
19001     
19002     initEvents : function()
19003     {
19004         if(!this.store){
19005             throw "can not find store for calendar";
19006         }
19007         
19008         var mark = {
19009             tag: "div",
19010             cls:"x-dlg-mask",
19011             style: "text-align:center",
19012             cn: [
19013                 {
19014                     tag: "div",
19015                     style: "background-color:white;width:50%;margin:250 auto",
19016                     cn: [
19017                         {
19018                             tag: "img",
19019                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19020                         },
19021                         {
19022                             tag: "span",
19023                             html: "Loading"
19024                         }
19025                         
19026                     ]
19027                 }
19028             ]
19029         };
19030         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19031         
19032         var size = this.el.select('.fc-content', true).first().getSize();
19033         this.maskEl.setSize(size.width, size.height);
19034         this.maskEl.enableDisplayMode("block");
19035         if(!this.loadMask){
19036             this.maskEl.hide();
19037         }
19038         
19039         this.store = Roo.factory(this.store, Roo.data);
19040         this.store.on('load', this.onLoad, this);
19041         this.store.on('beforeload', this.onBeforeLoad, this);
19042         
19043         this.resize();
19044         
19045         this.cells = this.el.select('.fc-day',true);
19046         //Roo.log(this.cells);
19047         this.textNodes = this.el.query('.fc-day-number');
19048         this.cells.addClassOnOver('fc-state-hover');
19049         
19050         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19051         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19052         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19053         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19054         
19055         this.on('monthchange', this.onMonthChange, this);
19056         
19057         this.update(new Date().clearTime());
19058     },
19059     
19060     resize : function() {
19061         var sz  = this.el.getSize();
19062         
19063         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19064         this.el.select('.fc-day-content div',true).setHeight(34);
19065     },
19066     
19067     
19068     // private
19069     showPrevMonth : function(e){
19070         this.update(this.activeDate.add("mo", -1));
19071     },
19072     showToday : function(e){
19073         this.update(new Date().clearTime());
19074     },
19075     // private
19076     showNextMonth : function(e){
19077         this.update(this.activeDate.add("mo", 1));
19078     },
19079
19080     // private
19081     showPrevYear : function(){
19082         this.update(this.activeDate.add("y", -1));
19083     },
19084
19085     // private
19086     showNextYear : function(){
19087         this.update(this.activeDate.add("y", 1));
19088     },
19089
19090     
19091    // private
19092     update : function(date)
19093     {
19094         var vd = this.activeDate;
19095         this.activeDate = date;
19096 //        if(vd && this.el){
19097 //            var t = date.getTime();
19098 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19099 //                Roo.log('using add remove');
19100 //                
19101 //                this.fireEvent('monthchange', this, date);
19102 //                
19103 //                this.cells.removeClass("fc-state-highlight");
19104 //                this.cells.each(function(c){
19105 //                   if(c.dateValue == t){
19106 //                       c.addClass("fc-state-highlight");
19107 //                       setTimeout(function(){
19108 //                            try{c.dom.firstChild.focus();}catch(e){}
19109 //                       }, 50);
19110 //                       return false;
19111 //                   }
19112 //                   return true;
19113 //                });
19114 //                return;
19115 //            }
19116 //        }
19117         
19118         var days = date.getDaysInMonth();
19119         
19120         var firstOfMonth = date.getFirstDateOfMonth();
19121         var startingPos = firstOfMonth.getDay()-this.startDay;
19122         
19123         if(startingPos < this.startDay){
19124             startingPos += 7;
19125         }
19126         
19127         var pm = date.add(Date.MONTH, -1);
19128         var prevStart = pm.getDaysInMonth()-startingPos;
19129 //        
19130         this.cells = this.el.select('.fc-day',true);
19131         this.textNodes = this.el.query('.fc-day-number');
19132         this.cells.addClassOnOver('fc-state-hover');
19133         
19134         var cells = this.cells.elements;
19135         var textEls = this.textNodes;
19136         
19137         Roo.each(cells, function(cell){
19138             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19139         });
19140         
19141         days += startingPos;
19142
19143         // convert everything to numbers so it's fast
19144         var day = 86400000;
19145         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19146         //Roo.log(d);
19147         //Roo.log(pm);
19148         //Roo.log(prevStart);
19149         
19150         var today = new Date().clearTime().getTime();
19151         var sel = date.clearTime().getTime();
19152         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19153         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19154         var ddMatch = this.disabledDatesRE;
19155         var ddText = this.disabledDatesText;
19156         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19157         var ddaysText = this.disabledDaysText;
19158         var format = this.format;
19159         
19160         var setCellClass = function(cal, cell){
19161             cell.row = 0;
19162             cell.events = [];
19163             cell.more = [];
19164             //Roo.log('set Cell Class');
19165             cell.title = "";
19166             var t = d.getTime();
19167             
19168             //Roo.log(d);
19169             
19170             cell.dateValue = t;
19171             if(t == today){
19172                 cell.className += " fc-today";
19173                 cell.className += " fc-state-highlight";
19174                 cell.title = cal.todayText;
19175             }
19176             if(t == sel){
19177                 // disable highlight in other month..
19178                 //cell.className += " fc-state-highlight";
19179                 
19180             }
19181             // disabling
19182             if(t < min) {
19183                 cell.className = " fc-state-disabled";
19184                 cell.title = cal.minText;
19185                 return;
19186             }
19187             if(t > max) {
19188                 cell.className = " fc-state-disabled";
19189                 cell.title = cal.maxText;
19190                 return;
19191             }
19192             if(ddays){
19193                 if(ddays.indexOf(d.getDay()) != -1){
19194                     cell.title = ddaysText;
19195                     cell.className = " fc-state-disabled";
19196                 }
19197             }
19198             if(ddMatch && format){
19199                 var fvalue = d.dateFormat(format);
19200                 if(ddMatch.test(fvalue)){
19201                     cell.title = ddText.replace("%0", fvalue);
19202                     cell.className = " fc-state-disabled";
19203                 }
19204             }
19205             
19206             if (!cell.initialClassName) {
19207                 cell.initialClassName = cell.dom.className;
19208             }
19209             
19210             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19211         };
19212
19213         var i = 0;
19214         
19215         for(; i < startingPos; i++) {
19216             textEls[i].innerHTML = (++prevStart);
19217             d.setDate(d.getDate()+1);
19218             
19219             cells[i].className = "fc-past fc-other-month";
19220             setCellClass(this, cells[i]);
19221         }
19222         
19223         var intDay = 0;
19224         
19225         for(; i < days; i++){
19226             intDay = i - startingPos + 1;
19227             textEls[i].innerHTML = (intDay);
19228             d.setDate(d.getDate()+1);
19229             
19230             cells[i].className = ''; // "x-date-active";
19231             setCellClass(this, cells[i]);
19232         }
19233         var extraDays = 0;
19234         
19235         for(; i < 42; i++) {
19236             textEls[i].innerHTML = (++extraDays);
19237             d.setDate(d.getDate()+1);
19238             
19239             cells[i].className = "fc-future fc-other-month";
19240             setCellClass(this, cells[i]);
19241         }
19242         
19243         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19244         
19245         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19246         
19247         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19248         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19249         
19250         if(totalRows != 6){
19251             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19252             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19253         }
19254         
19255         this.fireEvent('monthchange', this, date);
19256         
19257         
19258         /*
19259         if(!this.internalRender){
19260             var main = this.el.dom.firstChild;
19261             var w = main.offsetWidth;
19262             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19263             Roo.fly(main).setWidth(w);
19264             this.internalRender = true;
19265             // opera does not respect the auto grow header center column
19266             // then, after it gets a width opera refuses to recalculate
19267             // without a second pass
19268             if(Roo.isOpera && !this.secondPass){
19269                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19270                 this.secondPass = true;
19271                 this.update.defer(10, this, [date]);
19272             }
19273         }
19274         */
19275         
19276     },
19277     
19278     findCell : function(dt) {
19279         dt = dt.clearTime().getTime();
19280         var ret = false;
19281         this.cells.each(function(c){
19282             //Roo.log("check " +c.dateValue + '?=' + dt);
19283             if(c.dateValue == dt){
19284                 ret = c;
19285                 return false;
19286             }
19287             return true;
19288         });
19289         
19290         return ret;
19291     },
19292     
19293     findCells : function(ev) {
19294         var s = ev.start.clone().clearTime().getTime();
19295        // Roo.log(s);
19296         var e= ev.end.clone().clearTime().getTime();
19297        // Roo.log(e);
19298         var ret = [];
19299         this.cells.each(function(c){
19300              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19301             
19302             if(c.dateValue > e){
19303                 return ;
19304             }
19305             if(c.dateValue < s){
19306                 return ;
19307             }
19308             ret.push(c);
19309         });
19310         
19311         return ret;    
19312     },
19313     
19314 //    findBestRow: function(cells)
19315 //    {
19316 //        var ret = 0;
19317 //        
19318 //        for (var i =0 ; i < cells.length;i++) {
19319 //            ret  = Math.max(cells[i].rows || 0,ret);
19320 //        }
19321 //        return ret;
19322 //        
19323 //    },
19324     
19325     
19326     addItem : function(ev)
19327     {
19328         // look for vertical location slot in
19329         var cells = this.findCells(ev);
19330         
19331 //        ev.row = this.findBestRow(cells);
19332         
19333         // work out the location.
19334         
19335         var crow = false;
19336         var rows = [];
19337         for(var i =0; i < cells.length; i++) {
19338             
19339             cells[i].row = cells[0].row;
19340             
19341             if(i == 0){
19342                 cells[i].row = cells[i].row + 1;
19343             }
19344             
19345             if (!crow) {
19346                 crow = {
19347                     start : cells[i],
19348                     end :  cells[i]
19349                 };
19350                 continue;
19351             }
19352             if (crow.start.getY() == cells[i].getY()) {
19353                 // on same row.
19354                 crow.end = cells[i];
19355                 continue;
19356             }
19357             // different row.
19358             rows.push(crow);
19359             crow = {
19360                 start: cells[i],
19361                 end : cells[i]
19362             };
19363             
19364         }
19365         
19366         rows.push(crow);
19367         ev.els = [];
19368         ev.rows = rows;
19369         ev.cells = cells;
19370         
19371         cells[0].events.push(ev);
19372         
19373         this.calevents.push(ev);
19374     },
19375     
19376     clearEvents: function() {
19377         
19378         if(!this.calevents){
19379             return;
19380         }
19381         
19382         Roo.each(this.cells.elements, function(c){
19383             c.row = 0;
19384             c.events = [];
19385             c.more = [];
19386         });
19387         
19388         Roo.each(this.calevents, function(e) {
19389             Roo.each(e.els, function(el) {
19390                 el.un('mouseenter' ,this.onEventEnter, this);
19391                 el.un('mouseleave' ,this.onEventLeave, this);
19392                 el.remove();
19393             },this);
19394         },this);
19395         
19396         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19397             e.remove();
19398         });
19399         
19400     },
19401     
19402     renderEvents: function()
19403     {   
19404         var _this = this;
19405         
19406         this.cells.each(function(c) {
19407             
19408             if(c.row < 5){
19409                 return;
19410             }
19411             
19412             var ev = c.events;
19413             
19414             var r = 4;
19415             if(c.row != c.events.length){
19416                 r = 4 - (4 - (c.row - c.events.length));
19417             }
19418             
19419             c.events = ev.slice(0, r);
19420             c.more = ev.slice(r);
19421             
19422             if(c.more.length && c.more.length == 1){
19423                 c.events.push(c.more.pop());
19424             }
19425             
19426             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19427             
19428         });
19429             
19430         this.cells.each(function(c) {
19431             
19432             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19433             
19434             
19435             for (var e = 0; e < c.events.length; e++){
19436                 var ev = c.events[e];
19437                 var rows = ev.rows;
19438                 
19439                 for(var i = 0; i < rows.length; i++) {
19440                 
19441                     // how many rows should it span..
19442
19443                     var  cfg = {
19444                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19445                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19446
19447                         unselectable : "on",
19448                         cn : [
19449                             {
19450                                 cls: 'fc-event-inner',
19451                                 cn : [
19452     //                                {
19453     //                                  tag:'span',
19454     //                                  cls: 'fc-event-time',
19455     //                                  html : cells.length > 1 ? '' : ev.time
19456     //                                },
19457                                     {
19458                                       tag:'span',
19459                                       cls: 'fc-event-title',
19460                                       html : String.format('{0}', ev.title)
19461                                     }
19462
19463
19464                                 ]
19465                             },
19466                             {
19467                                 cls: 'ui-resizable-handle ui-resizable-e',
19468                                 html : '&nbsp;&nbsp;&nbsp'
19469                             }
19470
19471                         ]
19472                     };
19473
19474                     if (i == 0) {
19475                         cfg.cls += ' fc-event-start';
19476                     }
19477                     if ((i+1) == rows.length) {
19478                         cfg.cls += ' fc-event-end';
19479                     }
19480
19481                     var ctr = _this.el.select('.fc-event-container',true).first();
19482                     var cg = ctr.createChild(cfg);
19483
19484                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19485                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19486
19487                     var r = (c.more.length) ? 1 : 0;
19488                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19489                     cg.setWidth(ebox.right - sbox.x -2);
19490
19491                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19492                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19493                     cg.on('click', _this.onEventClick, _this, ev);
19494
19495                     ev.els.push(cg);
19496                     
19497                 }
19498                 
19499             }
19500             
19501             
19502             if(c.more.length){
19503                 var  cfg = {
19504                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19505                     style : 'position: absolute',
19506                     unselectable : "on",
19507                     cn : [
19508                         {
19509                             cls: 'fc-event-inner',
19510                             cn : [
19511                                 {
19512                                   tag:'span',
19513                                   cls: 'fc-event-title',
19514                                   html : 'More'
19515                                 }
19516
19517
19518                             ]
19519                         },
19520                         {
19521                             cls: 'ui-resizable-handle ui-resizable-e',
19522                             html : '&nbsp;&nbsp;&nbsp'
19523                         }
19524
19525                     ]
19526                 };
19527
19528                 var ctr = _this.el.select('.fc-event-container',true).first();
19529                 var cg = ctr.createChild(cfg);
19530
19531                 var sbox = c.select('.fc-day-content',true).first().getBox();
19532                 var ebox = c.select('.fc-day-content',true).first().getBox();
19533                 //Roo.log(cg);
19534                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19535                 cg.setWidth(ebox.right - sbox.x -2);
19536
19537                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19538                 
19539             }
19540             
19541         });
19542         
19543         
19544         
19545     },
19546     
19547     onEventEnter: function (e, el,event,d) {
19548         this.fireEvent('evententer', this, el, event);
19549     },
19550     
19551     onEventLeave: function (e, el,event,d) {
19552         this.fireEvent('eventleave', this, el, event);
19553     },
19554     
19555     onEventClick: function (e, el,event,d) {
19556         this.fireEvent('eventclick', this, el, event);
19557     },
19558     
19559     onMonthChange: function () {
19560         this.store.load();
19561     },
19562     
19563     onMoreEventClick: function(e, el, more)
19564     {
19565         var _this = this;
19566         
19567         this.calpopover.placement = 'right';
19568         this.calpopover.setTitle('More');
19569         
19570         this.calpopover.setContent('');
19571         
19572         var ctr = this.calpopover.el.select('.popover-content', true).first();
19573         
19574         Roo.each(more, function(m){
19575             var cfg = {
19576                 cls : 'fc-event-hori fc-event-draggable',
19577                 html : m.title
19578             };
19579             var cg = ctr.createChild(cfg);
19580             
19581             cg.on('click', _this.onEventClick, _this, m);
19582         });
19583         
19584         this.calpopover.show(el);
19585         
19586         
19587     },
19588     
19589     onLoad: function () 
19590     {   
19591         this.calevents = [];
19592         var cal = this;
19593         
19594         if(this.store.getCount() > 0){
19595             this.store.data.each(function(d){
19596                cal.addItem({
19597                     id : d.data.id,
19598                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19599                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19600                     time : d.data.start_time,
19601                     title : d.data.title,
19602                     description : d.data.description,
19603                     venue : d.data.venue
19604                 });
19605             });
19606         }
19607         
19608         this.renderEvents();
19609         
19610         if(this.calevents.length && this.loadMask){
19611             this.maskEl.hide();
19612         }
19613     },
19614     
19615     onBeforeLoad: function()
19616     {
19617         this.clearEvents();
19618         if(this.loadMask){
19619             this.maskEl.show();
19620         }
19621     }
19622 });
19623
19624  
19625  /*
19626  * - LGPL
19627  *
19628  * element
19629  * 
19630  */
19631
19632 /**
19633  * @class Roo.bootstrap.Popover
19634  * @extends Roo.bootstrap.Component
19635  * Bootstrap Popover class
19636  * @cfg {String} html contents of the popover   (or false to use children..)
19637  * @cfg {String} title of popover (or false to hide)
19638  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19639  * @cfg {String} trigger click || hover (or false to trigger manually)
19640  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19641  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19642  *      - if false and it has a 'parent' then it will be automatically added to that element
19643  *      - if string - Roo.get  will be called 
19644  * @cfg {Number} delay - delay before showing
19645  
19646  * @constructor
19647  * Create a new Popover
19648  * @param {Object} config The config object
19649  */
19650
19651 Roo.bootstrap.Popover = function(config){
19652     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19653     
19654     this.addEvents({
19655         // raw events
19656          /**
19657          * @event show
19658          * After the popover show
19659          * 
19660          * @param {Roo.bootstrap.Popover} this
19661          */
19662         "show" : true,
19663         /**
19664          * @event hide
19665          * After the popover hide
19666          * 
19667          * @param {Roo.bootstrap.Popover} this
19668          */
19669         "hide" : true
19670     });
19671 };
19672
19673 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19674     
19675     title: false,
19676     html: false,
19677     
19678     placement : 'right',
19679     trigger : 'hover', // hover
19680     modal : false,
19681     delay : 0,
19682     
19683     over: false,
19684     
19685     can_build_overlaid : false,
19686     
19687     maskEl : false, // the mask element
19688     headerEl : false,
19689     contentEl : false,
19690     alignEl : false, // when show is called with an element - this get's stored.
19691     
19692     getChildContainer : function()
19693     {
19694         return this.contentEl;
19695         
19696     },
19697     getPopoverHeader : function()
19698     {
19699         this.title = true; // flag not to hide it..
19700         this.headerEl.addClass('p-0');
19701         return this.headerEl
19702     },
19703     
19704     
19705     getAutoCreate : function(){
19706          
19707         var cfg = {
19708            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19709            style: 'display:block',
19710            cn : [
19711                 {
19712                     cls : 'arrow'
19713                 },
19714                 {
19715                     cls : 'popover-inner ',
19716                     cn : [
19717                         {
19718                             tag: 'h3',
19719                             cls: 'popover-title popover-header',
19720                             html : this.title === false ? '' : this.title
19721                         },
19722                         {
19723                             cls : 'popover-content popover-body '  + (this.cls || ''),
19724                             html : this.html || ''
19725                         }
19726                     ]
19727                     
19728                 }
19729            ]
19730         };
19731         
19732         return cfg;
19733     },
19734     /**
19735      * @param {string} the title
19736      */
19737     setTitle: function(str)
19738     {
19739         this.title = str;
19740         if (this.el) {
19741             this.headerEl.dom.innerHTML = str;
19742         }
19743         
19744     },
19745     /**
19746      * @param {string} the body content
19747      */
19748     setContent: function(str)
19749     {
19750         this.html = str;
19751         if (this.contentEl) {
19752             this.contentEl.dom.innerHTML = str;
19753         }
19754         
19755     },
19756     // as it get's added to the bottom of the page.
19757     onRender : function(ct, position)
19758     {
19759         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19760         
19761         
19762         
19763         if(!this.el){
19764             var cfg = Roo.apply({},  this.getAutoCreate());
19765             cfg.id = Roo.id();
19766             
19767             if (this.cls) {
19768                 cfg.cls += ' ' + this.cls;
19769             }
19770             if (this.style) {
19771                 cfg.style = this.style;
19772             }
19773             //Roo.log("adding to ");
19774             this.el = Roo.get(document.body).createChild(cfg, position);
19775 //            Roo.log(this.el);
19776         }
19777         
19778         this.contentEl = this.el.select('.popover-content',true).first();
19779         this.headerEl =  this.el.select('.popover-title',true).first();
19780         
19781         var nitems = [];
19782         if(typeof(this.items) != 'undefined'){
19783             var items = this.items;
19784             delete this.items;
19785
19786             for(var i =0;i < items.length;i++) {
19787                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19788             }
19789         }
19790
19791         this.items = nitems;
19792         
19793         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19794         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19795         
19796         
19797         
19798         this.initEvents();
19799     },
19800     
19801     resizeMask : function()
19802     {
19803         this.maskEl.setSize(
19804             Roo.lib.Dom.getViewWidth(true),
19805             Roo.lib.Dom.getViewHeight(true)
19806         );
19807     },
19808     
19809     initEvents : function()
19810     {
19811         
19812         if (!this.modal) { 
19813             Roo.bootstrap.Popover.register(this);
19814         }
19815          
19816         this.arrowEl = this.el.select('.arrow',true).first();
19817         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19818         this.el.enableDisplayMode('block');
19819         this.el.hide();
19820  
19821         
19822         if (this.over === false && !this.parent()) {
19823             return; 
19824         }
19825         if (this.triggers === false) {
19826             return;
19827         }
19828          
19829         // support parent
19830         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19831         var triggers = this.trigger ? this.trigger.split(' ') : [];
19832         Roo.each(triggers, function(trigger) {
19833         
19834             if (trigger == 'click') {
19835                 on_el.on('click', this.toggle, this);
19836             } else if (trigger != 'manual') {
19837                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19838                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19839       
19840                 on_el.on(eventIn  ,this.enter, this);
19841                 on_el.on(eventOut, this.leave, this);
19842             }
19843         }, this);
19844     },
19845     
19846     
19847     // private
19848     timeout : null,
19849     hoverState : null,
19850     
19851     toggle : function () {
19852         this.hoverState == 'in' ? this.leave() : this.enter();
19853     },
19854     
19855     enter : function () {
19856         
19857         clearTimeout(this.timeout);
19858     
19859         this.hoverState = 'in';
19860     
19861         if (!this.delay || !this.delay.show) {
19862             this.show();
19863             return;
19864         }
19865         var _t = this;
19866         this.timeout = setTimeout(function () {
19867             if (_t.hoverState == 'in') {
19868                 _t.show();
19869             }
19870         }, this.delay.show)
19871     },
19872     
19873     leave : function() {
19874         clearTimeout(this.timeout);
19875     
19876         this.hoverState = 'out';
19877     
19878         if (!this.delay || !this.delay.hide) {
19879             this.hide();
19880             return;
19881         }
19882         var _t = this;
19883         this.timeout = setTimeout(function () {
19884             if (_t.hoverState == 'out') {
19885                 _t.hide();
19886             }
19887         }, this.delay.hide)
19888     },
19889     /**
19890      * Show the popover
19891      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19892      * @param {string} (left|right|top|bottom) position
19893      */
19894     show : function (on_el, placement)
19895     {
19896         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19897         on_el = on_el || false; // default to false
19898          
19899         if (!on_el) {
19900             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19901                 on_el = this.parent().el;
19902             } else if (this.over) {
19903                 Roo.get(this.over);
19904             }
19905             
19906         }
19907         
19908         this.alignEl = Roo.get( on_el );
19909
19910         if (!this.el) {
19911             this.render(document.body);
19912         }
19913         
19914         
19915          
19916         
19917         if (this.title === false) {
19918             this.headerEl.hide();
19919         }
19920         
19921        
19922         this.el.show();
19923         this.el.dom.style.display = 'block';
19924          
19925  
19926         if (this.alignEl) {
19927             this.updatePosition(this.placement, true);
19928              
19929         } else {
19930             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19931             var es = this.el.getSize();
19932             var x = Roo.lib.Dom.getViewWidth()/2;
19933             var y = Roo.lib.Dom.getViewHeight()/2;
19934             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19935             
19936         }
19937
19938         
19939         //var arrow = this.el.select('.arrow',true).first();
19940         //arrow.set(align[2], 
19941         
19942         this.el.addClass('in');
19943         
19944          
19945         
19946         this.hoverState = 'in';
19947         
19948         if (this.modal) {
19949             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19950             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19951             this.maskEl.dom.style.display = 'block';
19952             this.maskEl.addClass('show');
19953         }
19954         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19955  
19956         this.fireEvent('show', this);
19957         
19958     },
19959     /**
19960      * fire this manually after loading a grid in the table for example
19961      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19962      * @param {Boolean} try and move it if we cant get right position.
19963      */
19964     updatePosition : function(placement, try_move)
19965     {
19966         // allow for calling with no parameters
19967         placement = placement   ? placement :  this.placement;
19968         try_move = typeof(try_move) == 'undefined' ? true : try_move;
19969         
19970         this.el.removeClass([
19971             'fade','top','bottom', 'left', 'right','in',
19972             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19973         ]);
19974         this.el.addClass(placement + ' bs-popover-' + placement);
19975         
19976         if (!this.alignEl ) {
19977             return false;
19978         }
19979         
19980         switch (placement) {
19981             case 'right':
19982                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19983                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19984                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19985                     //normal display... or moved up/down.
19986                     this.el.setXY(offset);
19987                     var xy = this.alignEl.getAnchorXY('tr', false);
19988                     xy[0]+=2;xy[1]+=5;
19989                     this.arrowEl.setXY(xy);
19990                     return true;
19991                 }
19992                 // continue through...
19993                 return this.updatePosition('left', false);
19994                 
19995             
19996             case 'left':
19997                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19998                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19999                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20000                     //normal display... or moved up/down.
20001                     this.el.setXY(offset);
20002                     var xy = this.alignEl.getAnchorXY('tl', false);
20003                     xy[0]-=10;xy[1]+=5; // << fix me
20004                     this.arrowEl.setXY(xy);
20005                     return true;
20006                 }
20007                 // call self...
20008                 return this.updatePosition('right', false);
20009             
20010             case 'top':
20011                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20012                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20013                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20014                     //normal display... or moved up/down.
20015                     this.el.setXY(offset);
20016                     var xy = this.alignEl.getAnchorXY('t', false);
20017                     xy[1]-=10; // << fix me
20018                     this.arrowEl.setXY(xy);
20019                     return true;
20020                 }
20021                 // fall through
20022                return this.updatePosition('bottom', false);
20023             
20024             case 'bottom':
20025                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20026                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20027                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20028                     //normal display... or moved up/down.
20029                     this.el.setXY(offset);
20030                     var xy = this.alignEl.getAnchorXY('b', false);
20031                      xy[1]+=2; // << fix me
20032                     this.arrowEl.setXY(xy);
20033                     return true;
20034                 }
20035                 // fall through
20036                 return this.updatePosition('top', false);
20037                 
20038             
20039         }
20040         
20041         
20042         return false;
20043     },
20044     
20045     hide : function()
20046     {
20047         this.el.setXY([0,0]);
20048         this.el.removeClass('in');
20049         this.el.hide();
20050         this.hoverState = null;
20051         this.maskEl.hide(); // always..
20052         this.fireEvent('hide', this);
20053     }
20054     
20055 });
20056
20057
20058 Roo.apply(Roo.bootstrap.Popover, {
20059
20060     alignment : {
20061         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20062         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20063         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20064         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20065     },
20066     
20067     zIndex : 20001,
20068
20069     clickHander : false,
20070     
20071
20072     onMouseDown : function(e)
20073     {
20074         if (!e.getTarget(".roo-popover")) {
20075             this.hideAll();
20076         }
20077          
20078     },
20079     
20080     popups : [],
20081     
20082     register : function(popup)
20083     {
20084         if (!Roo.bootstrap.Popover.clickHandler) {
20085             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20086         }
20087         // hide other popups.
20088         this.hideAll();
20089         this.popups.push(popup);
20090     },
20091     hideAll : function()
20092     {
20093         this.popups.forEach(function(p) {
20094             p.hide();
20095         });
20096     }
20097
20098 });/*
20099  * - LGPL
20100  *
20101  * Card header - holder for the card header elements.
20102  * 
20103  */
20104
20105 /**
20106  * @class Roo.bootstrap.PopoverNav
20107  * @extends Roo.bootstrap.NavGroup
20108  * Bootstrap Popover header navigation class
20109  * @constructor
20110  * Create a new Popover Header Navigation 
20111  * @param {Object} config The config object
20112  */
20113
20114 Roo.bootstrap.PopoverNav = function(config){
20115     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20116 };
20117
20118 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20119     
20120     
20121     container_method : 'getPopoverHeader' 
20122     
20123      
20124     
20125     
20126    
20127 });
20128
20129  
20130
20131  /*
20132  * - LGPL
20133  *
20134  * Progress
20135  * 
20136  */
20137
20138 /**
20139  * @class Roo.bootstrap.Progress
20140  * @extends Roo.bootstrap.Component
20141  * Bootstrap Progress class
20142  * @cfg {Boolean} striped striped of the progress bar
20143  * @cfg {Boolean} active animated of the progress bar
20144  * 
20145  * 
20146  * @constructor
20147  * Create a new Progress
20148  * @param {Object} config The config object
20149  */
20150
20151 Roo.bootstrap.Progress = function(config){
20152     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20153 };
20154
20155 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20156     
20157     striped : false,
20158     active: false,
20159     
20160     getAutoCreate : function(){
20161         var cfg = {
20162             tag: 'div',
20163             cls: 'progress'
20164         };
20165         
20166         
20167         if(this.striped){
20168             cfg.cls += ' progress-striped';
20169         }
20170       
20171         if(this.active){
20172             cfg.cls += ' active';
20173         }
20174         
20175         
20176         return cfg;
20177     }
20178    
20179 });
20180
20181  
20182
20183  /*
20184  * - LGPL
20185  *
20186  * ProgressBar
20187  * 
20188  */
20189
20190 /**
20191  * @class Roo.bootstrap.ProgressBar
20192  * @extends Roo.bootstrap.Component
20193  * Bootstrap ProgressBar class
20194  * @cfg {Number} aria_valuenow aria-value now
20195  * @cfg {Number} aria_valuemin aria-value min
20196  * @cfg {Number} aria_valuemax aria-value max
20197  * @cfg {String} label label for the progress bar
20198  * @cfg {String} panel (success | info | warning | danger )
20199  * @cfg {String} role role of the progress bar
20200  * @cfg {String} sr_only text
20201  * 
20202  * 
20203  * @constructor
20204  * Create a new ProgressBar
20205  * @param {Object} config The config object
20206  */
20207
20208 Roo.bootstrap.ProgressBar = function(config){
20209     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20210 };
20211
20212 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20213     
20214     aria_valuenow : 0,
20215     aria_valuemin : 0,
20216     aria_valuemax : 100,
20217     label : false,
20218     panel : false,
20219     role : false,
20220     sr_only: false,
20221     
20222     getAutoCreate : function()
20223     {
20224         
20225         var cfg = {
20226             tag: 'div',
20227             cls: 'progress-bar',
20228             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20229         };
20230         
20231         if(this.sr_only){
20232             cfg.cn = {
20233                 tag: 'span',
20234                 cls: 'sr-only',
20235                 html: this.sr_only
20236             }
20237         }
20238         
20239         if(this.role){
20240             cfg.role = this.role;
20241         }
20242         
20243         if(this.aria_valuenow){
20244             cfg['aria-valuenow'] = this.aria_valuenow;
20245         }
20246         
20247         if(this.aria_valuemin){
20248             cfg['aria-valuemin'] = this.aria_valuemin;
20249         }
20250         
20251         if(this.aria_valuemax){
20252             cfg['aria-valuemax'] = this.aria_valuemax;
20253         }
20254         
20255         if(this.label && !this.sr_only){
20256             cfg.html = this.label;
20257         }
20258         
20259         if(this.panel){
20260             cfg.cls += ' progress-bar-' + this.panel;
20261         }
20262         
20263         return cfg;
20264     },
20265     
20266     update : function(aria_valuenow)
20267     {
20268         this.aria_valuenow = aria_valuenow;
20269         
20270         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20271     }
20272    
20273 });
20274
20275  
20276
20277  /*
20278  * - LGPL
20279  *
20280  * column
20281  * 
20282  */
20283
20284 /**
20285  * @class Roo.bootstrap.TabGroup
20286  * @extends Roo.bootstrap.Column
20287  * Bootstrap Column class
20288  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20289  * @cfg {Boolean} carousel true to make the group behave like a carousel
20290  * @cfg {Boolean} bullets show bullets for the panels
20291  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20292  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20293  * @cfg {Boolean} showarrow (true|false) show arrow default true
20294  * 
20295  * @constructor
20296  * Create a new TabGroup
20297  * @param {Object} config The config object
20298  */
20299
20300 Roo.bootstrap.TabGroup = function(config){
20301     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20302     if (!this.navId) {
20303         this.navId = Roo.id();
20304     }
20305     this.tabs = [];
20306     Roo.bootstrap.TabGroup.register(this);
20307     
20308 };
20309
20310 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20311     
20312     carousel : false,
20313     transition : false,
20314     bullets : 0,
20315     timer : 0,
20316     autoslide : false,
20317     slideFn : false,
20318     slideOnTouch : false,
20319     showarrow : true,
20320     
20321     getAutoCreate : function()
20322     {
20323         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20324         
20325         cfg.cls += ' tab-content';
20326         
20327         if (this.carousel) {
20328             cfg.cls += ' carousel slide';
20329             
20330             cfg.cn = [{
20331                cls : 'carousel-inner',
20332                cn : []
20333             }];
20334         
20335             if(this.bullets  && !Roo.isTouch){
20336                 
20337                 var bullets = {
20338                     cls : 'carousel-bullets',
20339                     cn : []
20340                 };
20341                
20342                 if(this.bullets_cls){
20343                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20344                 }
20345                 
20346                 bullets.cn.push({
20347                     cls : 'clear'
20348                 });
20349                 
20350                 cfg.cn[0].cn.push(bullets);
20351             }
20352             
20353             if(this.showarrow){
20354                 cfg.cn[0].cn.push({
20355                     tag : 'div',
20356                     class : 'carousel-arrow',
20357                     cn : [
20358                         {
20359                             tag : 'div',
20360                             class : 'carousel-prev',
20361                             cn : [
20362                                 {
20363                                     tag : 'i',
20364                                     class : 'fa fa-chevron-left'
20365                                 }
20366                             ]
20367                         },
20368                         {
20369                             tag : 'div',
20370                             class : 'carousel-next',
20371                             cn : [
20372                                 {
20373                                     tag : 'i',
20374                                     class : 'fa fa-chevron-right'
20375                                 }
20376                             ]
20377                         }
20378                     ]
20379                 });
20380             }
20381             
20382         }
20383         
20384         return cfg;
20385     },
20386     
20387     initEvents:  function()
20388     {
20389 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20390 //            this.el.on("touchstart", this.onTouchStart, this);
20391 //        }
20392         
20393         if(this.autoslide){
20394             var _this = this;
20395             
20396             this.slideFn = window.setInterval(function() {
20397                 _this.showPanelNext();
20398             }, this.timer);
20399         }
20400         
20401         if(this.showarrow){
20402             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20403             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20404         }
20405         
20406         
20407     },
20408     
20409 //    onTouchStart : function(e, el, o)
20410 //    {
20411 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20412 //            return;
20413 //        }
20414 //        
20415 //        this.showPanelNext();
20416 //    },
20417     
20418     
20419     getChildContainer : function()
20420     {
20421         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20422     },
20423     
20424     /**
20425     * register a Navigation item
20426     * @param {Roo.bootstrap.NavItem} the navitem to add
20427     */
20428     register : function(item)
20429     {
20430         this.tabs.push( item);
20431         item.navId = this.navId; // not really needed..
20432         this.addBullet();
20433     
20434     },
20435     
20436     getActivePanel : function()
20437     {
20438         var r = false;
20439         Roo.each(this.tabs, function(t) {
20440             if (t.active) {
20441                 r = t;
20442                 return false;
20443             }
20444             return null;
20445         });
20446         return r;
20447         
20448     },
20449     getPanelByName : function(n)
20450     {
20451         var r = false;
20452         Roo.each(this.tabs, function(t) {
20453             if (t.tabId == n) {
20454                 r = t;
20455                 return false;
20456             }
20457             return null;
20458         });
20459         return r;
20460     },
20461     indexOfPanel : function(p)
20462     {
20463         var r = false;
20464         Roo.each(this.tabs, function(t,i) {
20465             if (t.tabId == p.tabId) {
20466                 r = i;
20467                 return false;
20468             }
20469             return null;
20470         });
20471         return r;
20472     },
20473     /**
20474      * show a specific panel
20475      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20476      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20477      */
20478     showPanel : function (pan)
20479     {
20480         if(this.transition || typeof(pan) == 'undefined'){
20481             Roo.log("waiting for the transitionend");
20482             return false;
20483         }
20484         
20485         if (typeof(pan) == 'number') {
20486             pan = this.tabs[pan];
20487         }
20488         
20489         if (typeof(pan) == 'string') {
20490             pan = this.getPanelByName(pan);
20491         }
20492         
20493         var cur = this.getActivePanel();
20494         
20495         if(!pan || !cur){
20496             Roo.log('pan or acitve pan is undefined');
20497             return false;
20498         }
20499         
20500         if (pan.tabId == this.getActivePanel().tabId) {
20501             return true;
20502         }
20503         
20504         if (false === cur.fireEvent('beforedeactivate')) {
20505             return false;
20506         }
20507         
20508         if(this.bullets > 0 && !Roo.isTouch){
20509             this.setActiveBullet(this.indexOfPanel(pan));
20510         }
20511         
20512         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20513             
20514             //class="carousel-item carousel-item-next carousel-item-left"
20515             
20516             this.transition = true;
20517             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20518             var lr = dir == 'next' ? 'left' : 'right';
20519             pan.el.addClass(dir); // or prev
20520             pan.el.addClass('carousel-item-' + dir); // or prev
20521             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20522             cur.el.addClass(lr); // or right
20523             pan.el.addClass(lr);
20524             cur.el.addClass('carousel-item-' +lr); // or right
20525             pan.el.addClass('carousel-item-' +lr);
20526             
20527             
20528             var _this = this;
20529             cur.el.on('transitionend', function() {
20530                 Roo.log("trans end?");
20531                 
20532                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20533                 pan.setActive(true);
20534                 
20535                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20536                 cur.setActive(false);
20537                 
20538                 _this.transition = false;
20539                 
20540             }, this, { single:  true } );
20541             
20542             return true;
20543         }
20544         
20545         cur.setActive(false);
20546         pan.setActive(true);
20547         
20548         return true;
20549         
20550     },
20551     showPanelNext : function()
20552     {
20553         var i = this.indexOfPanel(this.getActivePanel());
20554         
20555         if (i >= this.tabs.length - 1 && !this.autoslide) {
20556             return;
20557         }
20558         
20559         if (i >= this.tabs.length - 1 && this.autoslide) {
20560             i = -1;
20561         }
20562         
20563         this.showPanel(this.tabs[i+1]);
20564     },
20565     
20566     showPanelPrev : function()
20567     {
20568         var i = this.indexOfPanel(this.getActivePanel());
20569         
20570         if (i  < 1 && !this.autoslide) {
20571             return;
20572         }
20573         
20574         if (i < 1 && this.autoslide) {
20575             i = this.tabs.length;
20576         }
20577         
20578         this.showPanel(this.tabs[i-1]);
20579     },
20580     
20581     
20582     addBullet: function()
20583     {
20584         if(!this.bullets || Roo.isTouch){
20585             return;
20586         }
20587         var ctr = this.el.select('.carousel-bullets',true).first();
20588         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20589         var bullet = ctr.createChild({
20590             cls : 'bullet bullet-' + i
20591         },ctr.dom.lastChild);
20592         
20593         
20594         var _this = this;
20595         
20596         bullet.on('click', (function(e, el, o, ii, t){
20597
20598             e.preventDefault();
20599
20600             this.showPanel(ii);
20601
20602             if(this.autoslide && this.slideFn){
20603                 clearInterval(this.slideFn);
20604                 this.slideFn = window.setInterval(function() {
20605                     _this.showPanelNext();
20606                 }, this.timer);
20607             }
20608
20609         }).createDelegate(this, [i, bullet], true));
20610                 
20611         
20612     },
20613      
20614     setActiveBullet : function(i)
20615     {
20616         if(Roo.isTouch){
20617             return;
20618         }
20619         
20620         Roo.each(this.el.select('.bullet', true).elements, function(el){
20621             el.removeClass('selected');
20622         });
20623
20624         var bullet = this.el.select('.bullet-' + i, true).first();
20625         
20626         if(!bullet){
20627             return;
20628         }
20629         
20630         bullet.addClass('selected');
20631     }
20632     
20633     
20634   
20635 });
20636
20637  
20638
20639  
20640  
20641 Roo.apply(Roo.bootstrap.TabGroup, {
20642     
20643     groups: {},
20644      /**
20645     * register a Navigation Group
20646     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20647     */
20648     register : function(navgrp)
20649     {
20650         this.groups[navgrp.navId] = navgrp;
20651         
20652     },
20653     /**
20654     * fetch a Navigation Group based on the navigation ID
20655     * if one does not exist , it will get created.
20656     * @param {string} the navgroup to add
20657     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20658     */
20659     get: function(navId) {
20660         if (typeof(this.groups[navId]) == 'undefined') {
20661             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20662         }
20663         return this.groups[navId] ;
20664     }
20665     
20666     
20667     
20668 });
20669
20670  /*
20671  * - LGPL
20672  *
20673  * TabPanel
20674  * 
20675  */
20676
20677 /**
20678  * @class Roo.bootstrap.TabPanel
20679  * @extends Roo.bootstrap.Component
20680  * Bootstrap TabPanel class
20681  * @cfg {Boolean} active panel active
20682  * @cfg {String} html panel content
20683  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20684  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20685  * @cfg {String} href click to link..
20686  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20687  * 
20688  * 
20689  * @constructor
20690  * Create a new TabPanel
20691  * @param {Object} config The config object
20692  */
20693
20694 Roo.bootstrap.TabPanel = function(config){
20695     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20696     this.addEvents({
20697         /**
20698              * @event changed
20699              * Fires when the active status changes
20700              * @param {Roo.bootstrap.TabPanel} this
20701              * @param {Boolean} state the new state
20702             
20703          */
20704         'changed': true,
20705         /**
20706              * @event beforedeactivate
20707              * Fires before a tab is de-activated - can be used to do validation on a form.
20708              * @param {Roo.bootstrap.TabPanel} this
20709              * @return {Boolean} false if there is an error
20710             
20711          */
20712         'beforedeactivate': true
20713      });
20714     
20715     this.tabId = this.tabId || Roo.id();
20716   
20717 };
20718
20719 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20720     
20721     active: false,
20722     html: false,
20723     tabId: false,
20724     navId : false,
20725     href : '',
20726     touchSlide : false,
20727     getAutoCreate : function(){
20728         
20729         
20730         var cfg = {
20731             tag: 'div',
20732             // item is needed for carousel - not sure if it has any effect otherwise
20733             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20734             html: this.html || ''
20735         };
20736         
20737         if(this.active){
20738             cfg.cls += ' active';
20739         }
20740         
20741         if(this.tabId){
20742             cfg.tabId = this.tabId;
20743         }
20744         
20745         
20746         
20747         return cfg;
20748     },
20749     
20750     initEvents:  function()
20751     {
20752         var p = this.parent();
20753         
20754         this.navId = this.navId || p.navId;
20755         
20756         if (typeof(this.navId) != 'undefined') {
20757             // not really needed.. but just in case.. parent should be a NavGroup.
20758             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20759             
20760             tg.register(this);
20761             
20762             var i = tg.tabs.length - 1;
20763             
20764             if(this.active && tg.bullets > 0 && i < tg.bullets){
20765                 tg.setActiveBullet(i);
20766             }
20767         }
20768         
20769         this.el.on('click', this.onClick, this);
20770         
20771         if(Roo.isTouch && this.touchSlide){
20772             this.el.on("touchstart", this.onTouchStart, this);
20773             this.el.on("touchmove", this.onTouchMove, this);
20774             this.el.on("touchend", this.onTouchEnd, this);
20775         }
20776         
20777     },
20778     
20779     onRender : function(ct, position)
20780     {
20781         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20782     },
20783     
20784     setActive : function(state)
20785     {
20786         Roo.log("panel - set active " + this.tabId + "=" + state);
20787         
20788         this.active = state;
20789         if (!state) {
20790             this.el.removeClass('active');
20791             
20792         } else  if (!this.el.hasClass('active')) {
20793             this.el.addClass('active');
20794         }
20795         
20796         this.fireEvent('changed', this, state);
20797     },
20798     
20799     onClick : function(e)
20800     {
20801         e.preventDefault();
20802         
20803         if(!this.href.length){
20804             return;
20805         }
20806         
20807         window.location.href = this.href;
20808     },
20809     
20810     startX : 0,
20811     startY : 0,
20812     endX : 0,
20813     endY : 0,
20814     swiping : false,
20815     
20816     onTouchStart : function(e)
20817     {
20818         this.swiping = false;
20819         
20820         this.startX = e.browserEvent.touches[0].clientX;
20821         this.startY = e.browserEvent.touches[0].clientY;
20822     },
20823     
20824     onTouchMove : function(e)
20825     {
20826         this.swiping = true;
20827         
20828         this.endX = e.browserEvent.touches[0].clientX;
20829         this.endY = e.browserEvent.touches[0].clientY;
20830     },
20831     
20832     onTouchEnd : function(e)
20833     {
20834         if(!this.swiping){
20835             this.onClick(e);
20836             return;
20837         }
20838         
20839         var tabGroup = this.parent();
20840         
20841         if(this.endX > this.startX){ // swiping right
20842             tabGroup.showPanelPrev();
20843             return;
20844         }
20845         
20846         if(this.startX > this.endX){ // swiping left
20847             tabGroup.showPanelNext();
20848             return;
20849         }
20850     }
20851     
20852     
20853 });
20854  
20855
20856  
20857
20858  /*
20859  * - LGPL
20860  *
20861  * DateField
20862  * 
20863  */
20864
20865 /**
20866  * @class Roo.bootstrap.DateField
20867  * @extends Roo.bootstrap.Input
20868  * Bootstrap DateField class
20869  * @cfg {Number} weekStart default 0
20870  * @cfg {String} viewMode default empty, (months|years)
20871  * @cfg {String} minViewMode default empty, (months|years)
20872  * @cfg {Number} startDate default -Infinity
20873  * @cfg {Number} endDate default Infinity
20874  * @cfg {Boolean} todayHighlight default false
20875  * @cfg {Boolean} todayBtn default false
20876  * @cfg {Boolean} calendarWeeks default false
20877  * @cfg {Object} daysOfWeekDisabled default empty
20878  * @cfg {Boolean} singleMode default false (true | false)
20879  * 
20880  * @cfg {Boolean} keyboardNavigation default true
20881  * @cfg {String} language default en
20882  * 
20883  * @constructor
20884  * Create a new DateField
20885  * @param {Object} config The config object
20886  */
20887
20888 Roo.bootstrap.DateField = function(config){
20889     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20890      this.addEvents({
20891             /**
20892              * @event show
20893              * Fires when this field show.
20894              * @param {Roo.bootstrap.DateField} this
20895              * @param {Mixed} date The date value
20896              */
20897             show : true,
20898             /**
20899              * @event show
20900              * Fires when this field hide.
20901              * @param {Roo.bootstrap.DateField} this
20902              * @param {Mixed} date The date value
20903              */
20904             hide : true,
20905             /**
20906              * @event select
20907              * Fires when select a date.
20908              * @param {Roo.bootstrap.DateField} this
20909              * @param {Mixed} date The date value
20910              */
20911             select : true,
20912             /**
20913              * @event beforeselect
20914              * Fires when before select a date.
20915              * @param {Roo.bootstrap.DateField} this
20916              * @param {Mixed} date The date value
20917              */
20918             beforeselect : true
20919         });
20920 };
20921
20922 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20923     
20924     /**
20925      * @cfg {String} format
20926      * The default date format string which can be overriden for localization support.  The format must be
20927      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20928      */
20929     format : "m/d/y",
20930     /**
20931      * @cfg {String} altFormats
20932      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20933      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20934      */
20935     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20936     
20937     weekStart : 0,
20938     
20939     viewMode : '',
20940     
20941     minViewMode : '',
20942     
20943     todayHighlight : false,
20944     
20945     todayBtn: false,
20946     
20947     language: 'en',
20948     
20949     keyboardNavigation: true,
20950     
20951     calendarWeeks: false,
20952     
20953     startDate: -Infinity,
20954     
20955     endDate: Infinity,
20956     
20957     daysOfWeekDisabled: [],
20958     
20959     _events: [],
20960     
20961     singleMode : false,
20962     
20963     UTCDate: function()
20964     {
20965         return new Date(Date.UTC.apply(Date, arguments));
20966     },
20967     
20968     UTCToday: function()
20969     {
20970         var today = new Date();
20971         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20972     },
20973     
20974     getDate: function() {
20975             var d = this.getUTCDate();
20976             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20977     },
20978     
20979     getUTCDate: function() {
20980             return this.date;
20981     },
20982     
20983     setDate: function(d) {
20984             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20985     },
20986     
20987     setUTCDate: function(d) {
20988             this.date = d;
20989             this.setValue(this.formatDate(this.date));
20990     },
20991         
20992     onRender: function(ct, position)
20993     {
20994         
20995         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20996         
20997         this.language = this.language || 'en';
20998         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20999         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21000         
21001         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21002         this.format = this.format || 'm/d/y';
21003         this.isInline = false;
21004         this.isInput = true;
21005         this.component = this.el.select('.add-on', true).first() || false;
21006         this.component = (this.component && this.component.length === 0) ? false : this.component;
21007         this.hasInput = this.component && this.inputEl().length;
21008         
21009         if (typeof(this.minViewMode === 'string')) {
21010             switch (this.minViewMode) {
21011                 case 'months':
21012                     this.minViewMode = 1;
21013                     break;
21014                 case 'years':
21015                     this.minViewMode = 2;
21016                     break;
21017                 default:
21018                     this.minViewMode = 0;
21019                     break;
21020             }
21021         }
21022         
21023         if (typeof(this.viewMode === 'string')) {
21024             switch (this.viewMode) {
21025                 case 'months':
21026                     this.viewMode = 1;
21027                     break;
21028                 case 'years':
21029                     this.viewMode = 2;
21030                     break;
21031                 default:
21032                     this.viewMode = 0;
21033                     break;
21034             }
21035         }
21036                 
21037         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21038         
21039 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21040         
21041         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21042         
21043         this.picker().on('mousedown', this.onMousedown, this);
21044         this.picker().on('click', this.onClick, this);
21045         
21046         this.picker().addClass('datepicker-dropdown');
21047         
21048         this.startViewMode = this.viewMode;
21049         
21050         if(this.singleMode){
21051             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21052                 v.setVisibilityMode(Roo.Element.DISPLAY);
21053                 v.hide();
21054             });
21055             
21056             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21057                 v.setStyle('width', '189px');
21058             });
21059         }
21060         
21061         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21062             if(!this.calendarWeeks){
21063                 v.remove();
21064                 return;
21065             }
21066             
21067             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21068             v.attr('colspan', function(i, val){
21069                 return parseInt(val) + 1;
21070             });
21071         });
21072                         
21073         
21074         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21075         
21076         this.setStartDate(this.startDate);
21077         this.setEndDate(this.endDate);
21078         
21079         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21080         
21081         this.fillDow();
21082         this.fillMonths();
21083         this.update();
21084         this.showMode();
21085         
21086         if(this.isInline) {
21087             this.showPopup();
21088         }
21089     },
21090     
21091     picker : function()
21092     {
21093         return this.pickerEl;
21094 //        return this.el.select('.datepicker', true).first();
21095     },
21096     
21097     fillDow: function()
21098     {
21099         var dowCnt = this.weekStart;
21100         
21101         var dow = {
21102             tag: 'tr',
21103             cn: [
21104                 
21105             ]
21106         };
21107         
21108         if(this.calendarWeeks){
21109             dow.cn.push({
21110                 tag: 'th',
21111                 cls: 'cw',
21112                 html: '&nbsp;'
21113             })
21114         }
21115         
21116         while (dowCnt < this.weekStart + 7) {
21117             dow.cn.push({
21118                 tag: 'th',
21119                 cls: 'dow',
21120                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21121             });
21122         }
21123         
21124         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21125     },
21126     
21127     fillMonths: function()
21128     {    
21129         var i = 0;
21130         var months = this.picker().select('>.datepicker-months td', true).first();
21131         
21132         months.dom.innerHTML = '';
21133         
21134         while (i < 12) {
21135             var month = {
21136                 tag: 'span',
21137                 cls: 'month',
21138                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21139             };
21140             
21141             months.createChild(month);
21142         }
21143         
21144     },
21145     
21146     update: function()
21147     {
21148         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;
21149         
21150         if (this.date < this.startDate) {
21151             this.viewDate = new Date(this.startDate);
21152         } else if (this.date > this.endDate) {
21153             this.viewDate = new Date(this.endDate);
21154         } else {
21155             this.viewDate = new Date(this.date);
21156         }
21157         
21158         this.fill();
21159     },
21160     
21161     fill: function() 
21162     {
21163         var d = new Date(this.viewDate),
21164                 year = d.getUTCFullYear(),
21165                 month = d.getUTCMonth(),
21166                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21167                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21168                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21169                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21170                 currentDate = this.date && this.date.valueOf(),
21171                 today = this.UTCToday();
21172         
21173         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21174         
21175 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21176         
21177 //        this.picker.select('>tfoot th.today').
21178 //                                              .text(dates[this.language].today)
21179 //                                              .toggle(this.todayBtn !== false);
21180     
21181         this.updateNavArrows();
21182         this.fillMonths();
21183                                                 
21184         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21185         
21186         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21187          
21188         prevMonth.setUTCDate(day);
21189         
21190         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21191         
21192         var nextMonth = new Date(prevMonth);
21193         
21194         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21195         
21196         nextMonth = nextMonth.valueOf();
21197         
21198         var fillMonths = false;
21199         
21200         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21201         
21202         while(prevMonth.valueOf() <= nextMonth) {
21203             var clsName = '';
21204             
21205             if (prevMonth.getUTCDay() === this.weekStart) {
21206                 if(fillMonths){
21207                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21208                 }
21209                     
21210                 fillMonths = {
21211                     tag: 'tr',
21212                     cn: []
21213                 };
21214                 
21215                 if(this.calendarWeeks){
21216                     // ISO 8601: First week contains first thursday.
21217                     // ISO also states week starts on Monday, but we can be more abstract here.
21218                     var
21219                     // Start of current week: based on weekstart/current date
21220                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21221                     // Thursday of this week
21222                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21223                     // First Thursday of year, year from thursday
21224                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21225                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21226                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21227                     
21228                     fillMonths.cn.push({
21229                         tag: 'td',
21230                         cls: 'cw',
21231                         html: calWeek
21232                     });
21233                 }
21234             }
21235             
21236             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21237                 clsName += ' old';
21238             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21239                 clsName += ' new';
21240             }
21241             if (this.todayHighlight &&
21242                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21243                 prevMonth.getUTCMonth() == today.getMonth() &&
21244                 prevMonth.getUTCDate() == today.getDate()) {
21245                 clsName += ' today';
21246             }
21247             
21248             if (currentDate && prevMonth.valueOf() === currentDate) {
21249                 clsName += ' active';
21250             }
21251             
21252             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21253                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21254                     clsName += ' disabled';
21255             }
21256             
21257             fillMonths.cn.push({
21258                 tag: 'td',
21259                 cls: 'day ' + clsName,
21260                 html: prevMonth.getDate()
21261             });
21262             
21263             prevMonth.setDate(prevMonth.getDate()+1);
21264         }
21265           
21266         var currentYear = this.date && this.date.getUTCFullYear();
21267         var currentMonth = this.date && this.date.getUTCMonth();
21268         
21269         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21270         
21271         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21272             v.removeClass('active');
21273             
21274             if(currentYear === year && k === currentMonth){
21275                 v.addClass('active');
21276             }
21277             
21278             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21279                 v.addClass('disabled');
21280             }
21281             
21282         });
21283         
21284         
21285         year = parseInt(year/10, 10) * 10;
21286         
21287         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21288         
21289         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21290         
21291         year -= 1;
21292         for (var i = -1; i < 11; i++) {
21293             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21294                 tag: 'span',
21295                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21296                 html: year
21297             });
21298             
21299             year += 1;
21300         }
21301     },
21302     
21303     showMode: function(dir) 
21304     {
21305         if (dir) {
21306             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21307         }
21308         
21309         Roo.each(this.picker().select('>div',true).elements, function(v){
21310             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21311             v.hide();
21312         });
21313         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21314     },
21315     
21316     place: function()
21317     {
21318         if(this.isInline) {
21319             return;
21320         }
21321         
21322         this.picker().removeClass(['bottom', 'top']);
21323         
21324         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21325             /*
21326              * place to the top of element!
21327              *
21328              */
21329             
21330             this.picker().addClass('top');
21331             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21332             
21333             return;
21334         }
21335         
21336         this.picker().addClass('bottom');
21337         
21338         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21339     },
21340     
21341     parseDate : function(value)
21342     {
21343         if(!value || value instanceof Date){
21344             return value;
21345         }
21346         var v = Date.parseDate(value, this.format);
21347         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21348             v = Date.parseDate(value, 'Y-m-d');
21349         }
21350         if(!v && this.altFormats){
21351             if(!this.altFormatsArray){
21352                 this.altFormatsArray = this.altFormats.split("|");
21353             }
21354             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21355                 v = Date.parseDate(value, this.altFormatsArray[i]);
21356             }
21357         }
21358         return v;
21359     },
21360     
21361     formatDate : function(date, fmt)
21362     {   
21363         return (!date || !(date instanceof Date)) ?
21364         date : date.dateFormat(fmt || this.format);
21365     },
21366     
21367     onFocus : function()
21368     {
21369         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21370         this.showPopup();
21371     },
21372     
21373     onBlur : function()
21374     {
21375         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21376         
21377         var d = this.inputEl().getValue();
21378         
21379         this.setValue(d);
21380                 
21381         this.hidePopup();
21382     },
21383     
21384     showPopup : function()
21385     {
21386         this.picker().show();
21387         this.update();
21388         this.place();
21389         
21390         this.fireEvent('showpopup', this, this.date);
21391     },
21392     
21393     hidePopup : function()
21394     {
21395         if(this.isInline) {
21396             return;
21397         }
21398         this.picker().hide();
21399         this.viewMode = this.startViewMode;
21400         this.showMode();
21401         
21402         this.fireEvent('hidepopup', this, this.date);
21403         
21404     },
21405     
21406     onMousedown: function(e)
21407     {
21408         e.stopPropagation();
21409         e.preventDefault();
21410     },
21411     
21412     keyup: function(e)
21413     {
21414         Roo.bootstrap.DateField.superclass.keyup.call(this);
21415         this.update();
21416     },
21417
21418     setValue: function(v)
21419     {
21420         if(this.fireEvent('beforeselect', this, v) !== false){
21421             var d = new Date(this.parseDate(v) ).clearTime();
21422         
21423             if(isNaN(d.getTime())){
21424                 this.date = this.viewDate = '';
21425                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21426                 return;
21427             }
21428
21429             v = this.formatDate(d);
21430
21431             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21432
21433             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21434
21435             this.update();
21436
21437             this.fireEvent('select', this, this.date);
21438         }
21439     },
21440     
21441     getValue: function()
21442     {
21443         return this.formatDate(this.date);
21444     },
21445     
21446     fireKey: function(e)
21447     {
21448         if (!this.picker().isVisible()){
21449             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21450                 this.showPopup();
21451             }
21452             return;
21453         }
21454         
21455         var dateChanged = false,
21456         dir, day, month,
21457         newDate, newViewDate;
21458         
21459         switch(e.keyCode){
21460             case 27: // escape
21461                 this.hidePopup();
21462                 e.preventDefault();
21463                 break;
21464             case 37: // left
21465             case 39: // right
21466                 if (!this.keyboardNavigation) {
21467                     break;
21468                 }
21469                 dir = e.keyCode == 37 ? -1 : 1;
21470                 
21471                 if (e.ctrlKey){
21472                     newDate = this.moveYear(this.date, dir);
21473                     newViewDate = this.moveYear(this.viewDate, dir);
21474                 } else if (e.shiftKey){
21475                     newDate = this.moveMonth(this.date, dir);
21476                     newViewDate = this.moveMonth(this.viewDate, dir);
21477                 } else {
21478                     newDate = new Date(this.date);
21479                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21480                     newViewDate = new Date(this.viewDate);
21481                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21482                 }
21483                 if (this.dateWithinRange(newDate)){
21484                     this.date = newDate;
21485                     this.viewDate = newViewDate;
21486                     this.setValue(this.formatDate(this.date));
21487 //                    this.update();
21488                     e.preventDefault();
21489                     dateChanged = true;
21490                 }
21491                 break;
21492             case 38: // up
21493             case 40: // down
21494                 if (!this.keyboardNavigation) {
21495                     break;
21496                 }
21497                 dir = e.keyCode == 38 ? -1 : 1;
21498                 if (e.ctrlKey){
21499                     newDate = this.moveYear(this.date, dir);
21500                     newViewDate = this.moveYear(this.viewDate, dir);
21501                 } else if (e.shiftKey){
21502                     newDate = this.moveMonth(this.date, dir);
21503                     newViewDate = this.moveMonth(this.viewDate, dir);
21504                 } else {
21505                     newDate = new Date(this.date);
21506                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21507                     newViewDate = new Date(this.viewDate);
21508                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21509                 }
21510                 if (this.dateWithinRange(newDate)){
21511                     this.date = newDate;
21512                     this.viewDate = newViewDate;
21513                     this.setValue(this.formatDate(this.date));
21514 //                    this.update();
21515                     e.preventDefault();
21516                     dateChanged = true;
21517                 }
21518                 break;
21519             case 13: // enter
21520                 this.setValue(this.formatDate(this.date));
21521                 this.hidePopup();
21522                 e.preventDefault();
21523                 break;
21524             case 9: // tab
21525                 this.setValue(this.formatDate(this.date));
21526                 this.hidePopup();
21527                 break;
21528             case 16: // shift
21529             case 17: // ctrl
21530             case 18: // alt
21531                 break;
21532             default :
21533                 this.hidePopup();
21534                 
21535         }
21536     },
21537     
21538     
21539     onClick: function(e) 
21540     {
21541         e.stopPropagation();
21542         e.preventDefault();
21543         
21544         var target = e.getTarget();
21545         
21546         if(target.nodeName.toLowerCase() === 'i'){
21547             target = Roo.get(target).dom.parentNode;
21548         }
21549         
21550         var nodeName = target.nodeName;
21551         var className = target.className;
21552         var html = target.innerHTML;
21553         //Roo.log(nodeName);
21554         
21555         switch(nodeName.toLowerCase()) {
21556             case 'th':
21557                 switch(className) {
21558                     case 'switch':
21559                         this.showMode(1);
21560                         break;
21561                     case 'prev':
21562                     case 'next':
21563                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21564                         switch(this.viewMode){
21565                                 case 0:
21566                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21567                                         break;
21568                                 case 1:
21569                                 case 2:
21570                                         this.viewDate = this.moveYear(this.viewDate, dir);
21571                                         break;
21572                         }
21573                         this.fill();
21574                         break;
21575                     case 'today':
21576                         var date = new Date();
21577                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21578 //                        this.fill()
21579                         this.setValue(this.formatDate(this.date));
21580                         
21581                         this.hidePopup();
21582                         break;
21583                 }
21584                 break;
21585             case 'span':
21586                 if (className.indexOf('disabled') < 0) {
21587                     this.viewDate.setUTCDate(1);
21588                     if (className.indexOf('month') > -1) {
21589                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21590                     } else {
21591                         var year = parseInt(html, 10) || 0;
21592                         this.viewDate.setUTCFullYear(year);
21593                         
21594                     }
21595                     
21596                     if(this.singleMode){
21597                         this.setValue(this.formatDate(this.viewDate));
21598                         this.hidePopup();
21599                         return;
21600                     }
21601                     
21602                     this.showMode(-1);
21603                     this.fill();
21604                 }
21605                 break;
21606                 
21607             case 'td':
21608                 //Roo.log(className);
21609                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21610                     var day = parseInt(html, 10) || 1;
21611                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21612                         month = (this.viewDate || new Date()).getUTCMonth();
21613
21614                     if (className.indexOf('old') > -1) {
21615                         if(month === 0 ){
21616                             month = 11;
21617                             year -= 1;
21618                         }else{
21619                             month -= 1;
21620                         }
21621                     } else if (className.indexOf('new') > -1) {
21622                         if (month == 11) {
21623                             month = 0;
21624                             year += 1;
21625                         } else {
21626                             month += 1;
21627                         }
21628                     }
21629                     //Roo.log([year,month,day]);
21630                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21631                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21632 //                    this.fill();
21633                     //Roo.log(this.formatDate(this.date));
21634                     this.setValue(this.formatDate(this.date));
21635                     this.hidePopup();
21636                 }
21637                 break;
21638         }
21639     },
21640     
21641     setStartDate: function(startDate)
21642     {
21643         this.startDate = startDate || -Infinity;
21644         if (this.startDate !== -Infinity) {
21645             this.startDate = this.parseDate(this.startDate);
21646         }
21647         this.update();
21648         this.updateNavArrows();
21649     },
21650
21651     setEndDate: function(endDate)
21652     {
21653         this.endDate = endDate || Infinity;
21654         if (this.endDate !== Infinity) {
21655             this.endDate = this.parseDate(this.endDate);
21656         }
21657         this.update();
21658         this.updateNavArrows();
21659     },
21660     
21661     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21662     {
21663         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21664         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21665             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21666         }
21667         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21668             return parseInt(d, 10);
21669         });
21670         this.update();
21671         this.updateNavArrows();
21672     },
21673     
21674     updateNavArrows: function() 
21675     {
21676         if(this.singleMode){
21677             return;
21678         }
21679         
21680         var d = new Date(this.viewDate),
21681         year = d.getUTCFullYear(),
21682         month = d.getUTCMonth();
21683         
21684         Roo.each(this.picker().select('.prev', true).elements, function(v){
21685             v.show();
21686             switch (this.viewMode) {
21687                 case 0:
21688
21689                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21690                         v.hide();
21691                     }
21692                     break;
21693                 case 1:
21694                 case 2:
21695                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21696                         v.hide();
21697                     }
21698                     break;
21699             }
21700         });
21701         
21702         Roo.each(this.picker().select('.next', true).elements, function(v){
21703             v.show();
21704             switch (this.viewMode) {
21705                 case 0:
21706
21707                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21708                         v.hide();
21709                     }
21710                     break;
21711                 case 1:
21712                 case 2:
21713                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21714                         v.hide();
21715                     }
21716                     break;
21717             }
21718         })
21719     },
21720     
21721     moveMonth: function(date, dir)
21722     {
21723         if (!dir) {
21724             return date;
21725         }
21726         var new_date = new Date(date.valueOf()),
21727         day = new_date.getUTCDate(),
21728         month = new_date.getUTCMonth(),
21729         mag = Math.abs(dir),
21730         new_month, test;
21731         dir = dir > 0 ? 1 : -1;
21732         if (mag == 1){
21733             test = dir == -1
21734             // If going back one month, make sure month is not current month
21735             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21736             ? function(){
21737                 return new_date.getUTCMonth() == month;
21738             }
21739             // If going forward one month, make sure month is as expected
21740             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21741             : function(){
21742                 return new_date.getUTCMonth() != new_month;
21743             };
21744             new_month = month + dir;
21745             new_date.setUTCMonth(new_month);
21746             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21747             if (new_month < 0 || new_month > 11) {
21748                 new_month = (new_month + 12) % 12;
21749             }
21750         } else {
21751             // For magnitudes >1, move one month at a time...
21752             for (var i=0; i<mag; i++) {
21753                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21754                 new_date = this.moveMonth(new_date, dir);
21755             }
21756             // ...then reset the day, keeping it in the new month
21757             new_month = new_date.getUTCMonth();
21758             new_date.setUTCDate(day);
21759             test = function(){
21760                 return new_month != new_date.getUTCMonth();
21761             };
21762         }
21763         // Common date-resetting loop -- if date is beyond end of month, make it
21764         // end of month
21765         while (test()){
21766             new_date.setUTCDate(--day);
21767             new_date.setUTCMonth(new_month);
21768         }
21769         return new_date;
21770     },
21771
21772     moveYear: function(date, dir)
21773     {
21774         return this.moveMonth(date, dir*12);
21775     },
21776
21777     dateWithinRange: function(date)
21778     {
21779         return date >= this.startDate && date <= this.endDate;
21780     },
21781
21782     
21783     remove: function() 
21784     {
21785         this.picker().remove();
21786     },
21787     
21788     validateValue : function(value)
21789     {
21790         if(this.getVisibilityEl().hasClass('hidden')){
21791             return true;
21792         }
21793         
21794         if(value.length < 1)  {
21795             if(this.allowBlank){
21796                 return true;
21797             }
21798             return false;
21799         }
21800         
21801         if(value.length < this.minLength){
21802             return false;
21803         }
21804         if(value.length > this.maxLength){
21805             return false;
21806         }
21807         if(this.vtype){
21808             var vt = Roo.form.VTypes;
21809             if(!vt[this.vtype](value, this)){
21810                 return false;
21811             }
21812         }
21813         if(typeof this.validator == "function"){
21814             var msg = this.validator(value);
21815             if(msg !== true){
21816                 return false;
21817             }
21818         }
21819         
21820         if(this.regex && !this.regex.test(value)){
21821             return false;
21822         }
21823         
21824         if(typeof(this.parseDate(value)) == 'undefined'){
21825             return false;
21826         }
21827         
21828         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21829             return false;
21830         }      
21831         
21832         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21833             return false;
21834         } 
21835         
21836         
21837         return true;
21838     },
21839     
21840     reset : function()
21841     {
21842         this.date = this.viewDate = '';
21843         
21844         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21845     }
21846    
21847 });
21848
21849 Roo.apply(Roo.bootstrap.DateField,  {
21850     
21851     head : {
21852         tag: 'thead',
21853         cn: [
21854         {
21855             tag: 'tr',
21856             cn: [
21857             {
21858                 tag: 'th',
21859                 cls: 'prev',
21860                 html: '<i class="fa fa-arrow-left"/>'
21861             },
21862             {
21863                 tag: 'th',
21864                 cls: 'switch',
21865                 colspan: '5'
21866             },
21867             {
21868                 tag: 'th',
21869                 cls: 'next',
21870                 html: '<i class="fa fa-arrow-right"/>'
21871             }
21872
21873             ]
21874         }
21875         ]
21876     },
21877     
21878     content : {
21879         tag: 'tbody',
21880         cn: [
21881         {
21882             tag: 'tr',
21883             cn: [
21884             {
21885                 tag: 'td',
21886                 colspan: '7'
21887             }
21888             ]
21889         }
21890         ]
21891     },
21892     
21893     footer : {
21894         tag: 'tfoot',
21895         cn: [
21896         {
21897             tag: 'tr',
21898             cn: [
21899             {
21900                 tag: 'th',
21901                 colspan: '7',
21902                 cls: 'today'
21903             }
21904                     
21905             ]
21906         }
21907         ]
21908     },
21909     
21910     dates:{
21911         en: {
21912             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21913             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21914             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21915             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21916             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21917             today: "Today"
21918         }
21919     },
21920     
21921     modes: [
21922     {
21923         clsName: 'days',
21924         navFnc: 'Month',
21925         navStep: 1
21926     },
21927     {
21928         clsName: 'months',
21929         navFnc: 'FullYear',
21930         navStep: 1
21931     },
21932     {
21933         clsName: 'years',
21934         navFnc: 'FullYear',
21935         navStep: 10
21936     }]
21937 });
21938
21939 Roo.apply(Roo.bootstrap.DateField,  {
21940   
21941     template : {
21942         tag: 'div',
21943         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21944         cn: [
21945         {
21946             tag: 'div',
21947             cls: 'datepicker-days',
21948             cn: [
21949             {
21950                 tag: 'table',
21951                 cls: 'table-condensed',
21952                 cn:[
21953                 Roo.bootstrap.DateField.head,
21954                 {
21955                     tag: 'tbody'
21956                 },
21957                 Roo.bootstrap.DateField.footer
21958                 ]
21959             }
21960             ]
21961         },
21962         {
21963             tag: 'div',
21964             cls: 'datepicker-months',
21965             cn: [
21966             {
21967                 tag: 'table',
21968                 cls: 'table-condensed',
21969                 cn:[
21970                 Roo.bootstrap.DateField.head,
21971                 Roo.bootstrap.DateField.content,
21972                 Roo.bootstrap.DateField.footer
21973                 ]
21974             }
21975             ]
21976         },
21977         {
21978             tag: 'div',
21979             cls: 'datepicker-years',
21980             cn: [
21981             {
21982                 tag: 'table',
21983                 cls: 'table-condensed',
21984                 cn:[
21985                 Roo.bootstrap.DateField.head,
21986                 Roo.bootstrap.DateField.content,
21987                 Roo.bootstrap.DateField.footer
21988                 ]
21989             }
21990             ]
21991         }
21992         ]
21993     }
21994 });
21995
21996  
21997
21998  /*
21999  * - LGPL
22000  *
22001  * TimeField
22002  * 
22003  */
22004
22005 /**
22006  * @class Roo.bootstrap.TimeField
22007  * @extends Roo.bootstrap.Input
22008  * Bootstrap DateField class
22009  * 
22010  * 
22011  * @constructor
22012  * Create a new TimeField
22013  * @param {Object} config The config object
22014  */
22015
22016 Roo.bootstrap.TimeField = function(config){
22017     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22018     this.addEvents({
22019             /**
22020              * @event show
22021              * Fires when this field show.
22022              * @param {Roo.bootstrap.DateField} thisthis
22023              * @param {Mixed} date The date value
22024              */
22025             show : true,
22026             /**
22027              * @event show
22028              * Fires when this field hide.
22029              * @param {Roo.bootstrap.DateField} this
22030              * @param {Mixed} date The date value
22031              */
22032             hide : true,
22033             /**
22034              * @event select
22035              * Fires when select a date.
22036              * @param {Roo.bootstrap.DateField} this
22037              * @param {Mixed} date The date value
22038              */
22039             select : true
22040         });
22041 };
22042
22043 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22044     
22045     /**
22046      * @cfg {String} format
22047      * The default time format string which can be overriden for localization support.  The format must be
22048      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22049      */
22050     format : "H:i",
22051
22052     getAutoCreate : function()
22053     {
22054         this.after = '<i class="fa far fa-clock"></i>';
22055         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22056         
22057          
22058     },
22059     onRender: function(ct, position)
22060     {
22061         
22062         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22063                 
22064         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22065         
22066         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22067         
22068         this.pop = this.picker().select('>.datepicker-time',true).first();
22069         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22070         
22071         this.picker().on('mousedown', this.onMousedown, this);
22072         this.picker().on('click', this.onClick, this);
22073         
22074         this.picker().addClass('datepicker-dropdown');
22075     
22076         this.fillTime();
22077         this.update();
22078             
22079         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22080         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22081         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22082         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22083         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22084         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22085
22086     },
22087     
22088     fireKey: function(e){
22089         if (!this.picker().isVisible()){
22090             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22091                 this.show();
22092             }
22093             return;
22094         }
22095
22096         e.preventDefault();
22097         
22098         switch(e.keyCode){
22099             case 27: // escape
22100                 this.hide();
22101                 break;
22102             case 37: // left
22103             case 39: // right
22104                 this.onTogglePeriod();
22105                 break;
22106             case 38: // up
22107                 this.onIncrementMinutes();
22108                 break;
22109             case 40: // down
22110                 this.onDecrementMinutes();
22111                 break;
22112             case 13: // enter
22113             case 9: // tab
22114                 this.setTime();
22115                 break;
22116         }
22117     },
22118     
22119     onClick: function(e) {
22120         e.stopPropagation();
22121         e.preventDefault();
22122     },
22123     
22124     picker : function()
22125     {
22126         return this.pickerEl;
22127     },
22128     
22129     fillTime: function()
22130     {    
22131         var time = this.pop.select('tbody', true).first();
22132         
22133         time.dom.innerHTML = '';
22134         
22135         time.createChild({
22136             tag: 'tr',
22137             cn: [
22138                 {
22139                     tag: 'td',
22140                     cn: [
22141                         {
22142                             tag: 'a',
22143                             href: '#',
22144                             cls: 'btn',
22145                             cn: [
22146                                 {
22147                                     tag: 'i',
22148                                     cls: 'hours-up fa fas fa-chevron-up'
22149                                 }
22150                             ]
22151                         } 
22152                     ]
22153                 },
22154                 {
22155                     tag: 'td',
22156                     cls: 'separator'
22157                 },
22158                 {
22159                     tag: 'td',
22160                     cn: [
22161                         {
22162                             tag: 'a',
22163                             href: '#',
22164                             cls: 'btn',
22165                             cn: [
22166                                 {
22167                                     tag: 'i',
22168                                     cls: 'minutes-up fa fas fa-chevron-up'
22169                                 }
22170                             ]
22171                         }
22172                     ]
22173                 },
22174                 {
22175                     tag: 'td',
22176                     cls: 'separator'
22177                 }
22178             ]
22179         });
22180         
22181         time.createChild({
22182             tag: 'tr',
22183             cn: [
22184                 {
22185                     tag: 'td',
22186                     cn: [
22187                         {
22188                             tag: 'span',
22189                             cls: 'timepicker-hour',
22190                             html: '00'
22191                         }  
22192                     ]
22193                 },
22194                 {
22195                     tag: 'td',
22196                     cls: 'separator',
22197                     html: ':'
22198                 },
22199                 {
22200                     tag: 'td',
22201                     cn: [
22202                         {
22203                             tag: 'span',
22204                             cls: 'timepicker-minute',
22205                             html: '00'
22206                         }  
22207                     ]
22208                 },
22209                 {
22210                     tag: 'td',
22211                     cls: 'separator'
22212                 },
22213                 {
22214                     tag: 'td',
22215                     cn: [
22216                         {
22217                             tag: 'button',
22218                             type: 'button',
22219                             cls: 'btn btn-primary period',
22220                             html: 'AM'
22221                             
22222                         }
22223                     ]
22224                 }
22225             ]
22226         });
22227         
22228         time.createChild({
22229             tag: 'tr',
22230             cn: [
22231                 {
22232                     tag: 'td',
22233                     cn: [
22234                         {
22235                             tag: 'a',
22236                             href: '#',
22237                             cls: 'btn',
22238                             cn: [
22239                                 {
22240                                     tag: 'span',
22241                                     cls: 'hours-down fa fas fa-chevron-down'
22242                                 }
22243                             ]
22244                         }
22245                     ]
22246                 },
22247                 {
22248                     tag: 'td',
22249                     cls: 'separator'
22250                 },
22251                 {
22252                     tag: 'td',
22253                     cn: [
22254                         {
22255                             tag: 'a',
22256                             href: '#',
22257                             cls: 'btn',
22258                             cn: [
22259                                 {
22260                                     tag: 'span',
22261                                     cls: 'minutes-down fa fas fa-chevron-down'
22262                                 }
22263                             ]
22264                         }
22265                     ]
22266                 },
22267                 {
22268                     tag: 'td',
22269                     cls: 'separator'
22270                 }
22271             ]
22272         });
22273         
22274     },
22275     
22276     update: function()
22277     {
22278         
22279         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22280         
22281         this.fill();
22282     },
22283     
22284     fill: function() 
22285     {
22286         var hours = this.time.getHours();
22287         var minutes = this.time.getMinutes();
22288         var period = 'AM';
22289         
22290         if(hours > 11){
22291             period = 'PM';
22292         }
22293         
22294         if(hours == 0){
22295             hours = 12;
22296         }
22297         
22298         
22299         if(hours > 12){
22300             hours = hours - 12;
22301         }
22302         
22303         if(hours < 10){
22304             hours = '0' + hours;
22305         }
22306         
22307         if(minutes < 10){
22308             minutes = '0' + minutes;
22309         }
22310         
22311         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22312         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22313         this.pop.select('button', true).first().dom.innerHTML = period;
22314         
22315     },
22316     
22317     place: function()
22318     {   
22319         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22320         
22321         var cls = ['bottom'];
22322         
22323         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22324             cls.pop();
22325             cls.push('top');
22326         }
22327         
22328         cls.push('right');
22329         
22330         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22331             cls.pop();
22332             cls.push('left');
22333         }
22334         //this.picker().setXY(20000,20000);
22335         this.picker().addClass(cls.join('-'));
22336         
22337         var _this = this;
22338         
22339         Roo.each(cls, function(c){
22340             if(c == 'bottom'){
22341                 (function() {
22342                  //  
22343                 }).defer(200);
22344                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22345                 //_this.picker().setTop(_this.inputEl().getHeight());
22346                 return;
22347             }
22348             if(c == 'top'){
22349                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22350                 
22351                 //_this.picker().setTop(0 - _this.picker().getHeight());
22352                 return;
22353             }
22354             /*
22355             if(c == 'left'){
22356                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22357                 return;
22358             }
22359             if(c == 'right'){
22360                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22361                 return;
22362             }
22363             */
22364         });
22365         
22366     },
22367   
22368     onFocus : function()
22369     {
22370         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22371         this.show();
22372     },
22373     
22374     onBlur : function()
22375     {
22376         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22377         this.hide();
22378     },
22379     
22380     show : function()
22381     {
22382         this.picker().show();
22383         this.pop.show();
22384         this.update();
22385         this.place();
22386         
22387         this.fireEvent('show', this, this.date);
22388     },
22389     
22390     hide : function()
22391     {
22392         this.picker().hide();
22393         this.pop.hide();
22394         
22395         this.fireEvent('hide', this, this.date);
22396     },
22397     
22398     setTime : function()
22399     {
22400         this.hide();
22401         this.setValue(this.time.format(this.format));
22402         
22403         this.fireEvent('select', this, this.date);
22404         
22405         
22406     },
22407     
22408     onMousedown: function(e){
22409         e.stopPropagation();
22410         e.preventDefault();
22411     },
22412     
22413     onIncrementHours: function()
22414     {
22415         Roo.log('onIncrementHours');
22416         this.time = this.time.add(Date.HOUR, 1);
22417         this.update();
22418         
22419     },
22420     
22421     onDecrementHours: function()
22422     {
22423         Roo.log('onDecrementHours');
22424         this.time = this.time.add(Date.HOUR, -1);
22425         this.update();
22426     },
22427     
22428     onIncrementMinutes: function()
22429     {
22430         Roo.log('onIncrementMinutes');
22431         this.time = this.time.add(Date.MINUTE, 1);
22432         this.update();
22433     },
22434     
22435     onDecrementMinutes: function()
22436     {
22437         Roo.log('onDecrementMinutes');
22438         this.time = this.time.add(Date.MINUTE, -1);
22439         this.update();
22440     },
22441     
22442     onTogglePeriod: function()
22443     {
22444         Roo.log('onTogglePeriod');
22445         this.time = this.time.add(Date.HOUR, 12);
22446         this.update();
22447     }
22448     
22449    
22450 });
22451  
22452
22453 Roo.apply(Roo.bootstrap.TimeField,  {
22454   
22455     template : {
22456         tag: 'div',
22457         cls: 'datepicker dropdown-menu',
22458         cn: [
22459             {
22460                 tag: 'div',
22461                 cls: 'datepicker-time',
22462                 cn: [
22463                 {
22464                     tag: 'table',
22465                     cls: 'table-condensed',
22466                     cn:[
22467                         {
22468                             tag: 'tbody',
22469                             cn: [
22470                                 {
22471                                     tag: 'tr',
22472                                     cn: [
22473                                     {
22474                                         tag: 'td',
22475                                         colspan: '7'
22476                                     }
22477                                     ]
22478                                 }
22479                             ]
22480                         },
22481                         {
22482                             tag: 'tfoot',
22483                             cn: [
22484                                 {
22485                                     tag: 'tr',
22486                                     cn: [
22487                                     {
22488                                         tag: 'th',
22489                                         colspan: '7',
22490                                         cls: '',
22491                                         cn: [
22492                                             {
22493                                                 tag: 'button',
22494                                                 cls: 'btn btn-info ok',
22495                                                 html: 'OK'
22496                                             }
22497                                         ]
22498                                     }
22499                     
22500                                     ]
22501                                 }
22502                             ]
22503                         }
22504                     ]
22505                 }
22506                 ]
22507             }
22508         ]
22509     }
22510 });
22511
22512  
22513
22514  /*
22515  * - LGPL
22516  *
22517  * MonthField
22518  * 
22519  */
22520
22521 /**
22522  * @class Roo.bootstrap.MonthField
22523  * @extends Roo.bootstrap.Input
22524  * Bootstrap MonthField class
22525  * 
22526  * @cfg {String} language default en
22527  * 
22528  * @constructor
22529  * Create a new MonthField
22530  * @param {Object} config The config object
22531  */
22532
22533 Roo.bootstrap.MonthField = function(config){
22534     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22535     
22536     this.addEvents({
22537         /**
22538          * @event show
22539          * Fires when this field show.
22540          * @param {Roo.bootstrap.MonthField} this
22541          * @param {Mixed} date The date value
22542          */
22543         show : true,
22544         /**
22545          * @event show
22546          * Fires when this field hide.
22547          * @param {Roo.bootstrap.MonthField} this
22548          * @param {Mixed} date The date value
22549          */
22550         hide : true,
22551         /**
22552          * @event select
22553          * Fires when select a date.
22554          * @param {Roo.bootstrap.MonthField} this
22555          * @param {String} oldvalue The old value
22556          * @param {String} newvalue The new value
22557          */
22558         select : true
22559     });
22560 };
22561
22562 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22563     
22564     onRender: function(ct, position)
22565     {
22566         
22567         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22568         
22569         this.language = this.language || 'en';
22570         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22571         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22572         
22573         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22574         this.isInline = false;
22575         this.isInput = true;
22576         this.component = this.el.select('.add-on', true).first() || false;
22577         this.component = (this.component && this.component.length === 0) ? false : this.component;
22578         this.hasInput = this.component && this.inputEL().length;
22579         
22580         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22581         
22582         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22583         
22584         this.picker().on('mousedown', this.onMousedown, this);
22585         this.picker().on('click', this.onClick, this);
22586         
22587         this.picker().addClass('datepicker-dropdown');
22588         
22589         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22590             v.setStyle('width', '189px');
22591         });
22592         
22593         this.fillMonths();
22594         
22595         this.update();
22596         
22597         if(this.isInline) {
22598             this.show();
22599         }
22600         
22601     },
22602     
22603     setValue: function(v, suppressEvent)
22604     {   
22605         var o = this.getValue();
22606         
22607         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22608         
22609         this.update();
22610
22611         if(suppressEvent !== true){
22612             this.fireEvent('select', this, o, v);
22613         }
22614         
22615     },
22616     
22617     getValue: function()
22618     {
22619         return this.value;
22620     },
22621     
22622     onClick: function(e) 
22623     {
22624         e.stopPropagation();
22625         e.preventDefault();
22626         
22627         var target = e.getTarget();
22628         
22629         if(target.nodeName.toLowerCase() === 'i'){
22630             target = Roo.get(target).dom.parentNode;
22631         }
22632         
22633         var nodeName = target.nodeName;
22634         var className = target.className;
22635         var html = target.innerHTML;
22636         
22637         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22638             return;
22639         }
22640         
22641         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22642         
22643         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22644         
22645         this.hide();
22646                         
22647     },
22648     
22649     picker : function()
22650     {
22651         return this.pickerEl;
22652     },
22653     
22654     fillMonths: function()
22655     {    
22656         var i = 0;
22657         var months = this.picker().select('>.datepicker-months td', true).first();
22658         
22659         months.dom.innerHTML = '';
22660         
22661         while (i < 12) {
22662             var month = {
22663                 tag: 'span',
22664                 cls: 'month',
22665                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22666             };
22667             
22668             months.createChild(month);
22669         }
22670         
22671     },
22672     
22673     update: function()
22674     {
22675         var _this = this;
22676         
22677         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22678             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22679         }
22680         
22681         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22682             e.removeClass('active');
22683             
22684             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22685                 e.addClass('active');
22686             }
22687         })
22688     },
22689     
22690     place: function()
22691     {
22692         if(this.isInline) {
22693             return;
22694         }
22695         
22696         this.picker().removeClass(['bottom', 'top']);
22697         
22698         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22699             /*
22700              * place to the top of element!
22701              *
22702              */
22703             
22704             this.picker().addClass('top');
22705             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22706             
22707             return;
22708         }
22709         
22710         this.picker().addClass('bottom');
22711         
22712         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22713     },
22714     
22715     onFocus : function()
22716     {
22717         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22718         this.show();
22719     },
22720     
22721     onBlur : function()
22722     {
22723         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22724         
22725         var d = this.inputEl().getValue();
22726         
22727         this.setValue(d);
22728                 
22729         this.hide();
22730     },
22731     
22732     show : function()
22733     {
22734         this.picker().show();
22735         this.picker().select('>.datepicker-months', true).first().show();
22736         this.update();
22737         this.place();
22738         
22739         this.fireEvent('show', this, this.date);
22740     },
22741     
22742     hide : function()
22743     {
22744         if(this.isInline) {
22745             return;
22746         }
22747         this.picker().hide();
22748         this.fireEvent('hide', this, this.date);
22749         
22750     },
22751     
22752     onMousedown: function(e)
22753     {
22754         e.stopPropagation();
22755         e.preventDefault();
22756     },
22757     
22758     keyup: function(e)
22759     {
22760         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22761         this.update();
22762     },
22763
22764     fireKey: function(e)
22765     {
22766         if (!this.picker().isVisible()){
22767             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22768                 this.show();
22769             }
22770             return;
22771         }
22772         
22773         var dir;
22774         
22775         switch(e.keyCode){
22776             case 27: // escape
22777                 this.hide();
22778                 e.preventDefault();
22779                 break;
22780             case 37: // left
22781             case 39: // right
22782                 dir = e.keyCode == 37 ? -1 : 1;
22783                 
22784                 this.vIndex = this.vIndex + dir;
22785                 
22786                 if(this.vIndex < 0){
22787                     this.vIndex = 0;
22788                 }
22789                 
22790                 if(this.vIndex > 11){
22791                     this.vIndex = 11;
22792                 }
22793                 
22794                 if(isNaN(this.vIndex)){
22795                     this.vIndex = 0;
22796                 }
22797                 
22798                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22799                 
22800                 break;
22801             case 38: // up
22802             case 40: // down
22803                 
22804                 dir = e.keyCode == 38 ? -1 : 1;
22805                 
22806                 this.vIndex = this.vIndex + dir * 4;
22807                 
22808                 if(this.vIndex < 0){
22809                     this.vIndex = 0;
22810                 }
22811                 
22812                 if(this.vIndex > 11){
22813                     this.vIndex = 11;
22814                 }
22815                 
22816                 if(isNaN(this.vIndex)){
22817                     this.vIndex = 0;
22818                 }
22819                 
22820                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22821                 break;
22822                 
22823             case 13: // enter
22824                 
22825                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22826                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22827                 }
22828                 
22829                 this.hide();
22830                 e.preventDefault();
22831                 break;
22832             case 9: // tab
22833                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22834                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22835                 }
22836                 this.hide();
22837                 break;
22838             case 16: // shift
22839             case 17: // ctrl
22840             case 18: // alt
22841                 break;
22842             default :
22843                 this.hide();
22844                 
22845         }
22846     },
22847     
22848     remove: function() 
22849     {
22850         this.picker().remove();
22851     }
22852    
22853 });
22854
22855 Roo.apply(Roo.bootstrap.MonthField,  {
22856     
22857     content : {
22858         tag: 'tbody',
22859         cn: [
22860         {
22861             tag: 'tr',
22862             cn: [
22863             {
22864                 tag: 'td',
22865                 colspan: '7'
22866             }
22867             ]
22868         }
22869         ]
22870     },
22871     
22872     dates:{
22873         en: {
22874             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22875             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22876         }
22877     }
22878 });
22879
22880 Roo.apply(Roo.bootstrap.MonthField,  {
22881   
22882     template : {
22883         tag: 'div',
22884         cls: 'datepicker dropdown-menu roo-dynamic',
22885         cn: [
22886             {
22887                 tag: 'div',
22888                 cls: 'datepicker-months',
22889                 cn: [
22890                 {
22891                     tag: 'table',
22892                     cls: 'table-condensed',
22893                     cn:[
22894                         Roo.bootstrap.DateField.content
22895                     ]
22896                 }
22897                 ]
22898             }
22899         ]
22900     }
22901 });
22902
22903  
22904
22905  
22906  /*
22907  * - LGPL
22908  *
22909  * CheckBox
22910  * 
22911  */
22912
22913 /**
22914  * @class Roo.bootstrap.CheckBox
22915  * @extends Roo.bootstrap.Input
22916  * Bootstrap CheckBox class
22917  * 
22918  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22919  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22920  * @cfg {String} boxLabel The text that appears beside the checkbox
22921  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22922  * @cfg {Boolean} checked initnal the element
22923  * @cfg {Boolean} inline inline the element (default false)
22924  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22925  * @cfg {String} tooltip label tooltip
22926  * 
22927  * @constructor
22928  * Create a new CheckBox
22929  * @param {Object} config The config object
22930  */
22931
22932 Roo.bootstrap.CheckBox = function(config){
22933     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22934    
22935     this.addEvents({
22936         /**
22937         * @event check
22938         * Fires when the element is checked or unchecked.
22939         * @param {Roo.bootstrap.CheckBox} this This input
22940         * @param {Boolean} checked The new checked value
22941         */
22942        check : true,
22943        /**
22944         * @event click
22945         * Fires when the element is click.
22946         * @param {Roo.bootstrap.CheckBox} this This input
22947         */
22948        click : true
22949     });
22950     
22951 };
22952
22953 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22954   
22955     inputType: 'checkbox',
22956     inputValue: 1,
22957     valueOff: 0,
22958     boxLabel: false,
22959     checked: false,
22960     weight : false,
22961     inline: false,
22962     tooltip : '',
22963     
22964     // checkbox success does not make any sense really.. 
22965     invalidClass : "",
22966     validClass : "",
22967     
22968     
22969     getAutoCreate : function()
22970     {
22971         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22972         
22973         var id = Roo.id();
22974         
22975         var cfg = {};
22976         
22977         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22978         
22979         if(this.inline){
22980             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22981         }
22982         
22983         var input =  {
22984             tag: 'input',
22985             id : id,
22986             type : this.inputType,
22987             value : this.inputValue,
22988             cls : 'roo-' + this.inputType, //'form-box',
22989             placeholder : this.placeholder || ''
22990             
22991         };
22992         
22993         if(this.inputType != 'radio'){
22994             var hidden =  {
22995                 tag: 'input',
22996                 type : 'hidden',
22997                 cls : 'roo-hidden-value',
22998                 value : this.checked ? this.inputValue : this.valueOff
22999             };
23000         }
23001         
23002             
23003         if (this.weight) { // Validity check?
23004             cfg.cls += " " + this.inputType + "-" + this.weight;
23005         }
23006         
23007         if (this.disabled) {
23008             input.disabled=true;
23009         }
23010         
23011         if(this.checked){
23012             input.checked = this.checked;
23013         }
23014         
23015         if (this.name) {
23016             
23017             input.name = this.name;
23018             
23019             if(this.inputType != 'radio'){
23020                 hidden.name = this.name;
23021                 input.name = '_hidden_' + this.name;
23022             }
23023         }
23024         
23025         if (this.size) {
23026             input.cls += ' input-' + this.size;
23027         }
23028         
23029         var settings=this;
23030         
23031         ['xs','sm','md','lg'].map(function(size){
23032             if (settings[size]) {
23033                 cfg.cls += ' col-' + size + '-' + settings[size];
23034             }
23035         });
23036         
23037         var inputblock = input;
23038          
23039         if (this.before || this.after) {
23040             
23041             inputblock = {
23042                 cls : 'input-group',
23043                 cn :  [] 
23044             };
23045             
23046             if (this.before) {
23047                 inputblock.cn.push({
23048                     tag :'span',
23049                     cls : 'input-group-addon',
23050                     html : this.before
23051                 });
23052             }
23053             
23054             inputblock.cn.push(input);
23055             
23056             if(this.inputType != 'radio'){
23057                 inputblock.cn.push(hidden);
23058             }
23059             
23060             if (this.after) {
23061                 inputblock.cn.push({
23062                     tag :'span',
23063                     cls : 'input-group-addon',
23064                     html : this.after
23065                 });
23066             }
23067             
23068         }
23069         var boxLabelCfg = false;
23070         
23071         if(this.boxLabel){
23072            
23073             boxLabelCfg = {
23074                 tag: 'label',
23075                 //'for': id, // box label is handled by onclick - so no for...
23076                 cls: 'box-label',
23077                 html: this.boxLabel
23078             };
23079             if(this.tooltip){
23080                 boxLabelCfg.tooltip = this.tooltip;
23081             }
23082              
23083         }
23084         
23085         
23086         if (align ==='left' && this.fieldLabel.length) {
23087 //                Roo.log("left and has label");
23088             cfg.cn = [
23089                 {
23090                     tag: 'label',
23091                     'for' :  id,
23092                     cls : 'control-label',
23093                     html : this.fieldLabel
23094                 },
23095                 {
23096                     cls : "", 
23097                     cn: [
23098                         inputblock
23099                     ]
23100                 }
23101             ];
23102             
23103             if (boxLabelCfg) {
23104                 cfg.cn[1].cn.push(boxLabelCfg);
23105             }
23106             
23107             if(this.labelWidth > 12){
23108                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23109             }
23110             
23111             if(this.labelWidth < 13 && this.labelmd == 0){
23112                 this.labelmd = this.labelWidth;
23113             }
23114             
23115             if(this.labellg > 0){
23116                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23117                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23118             }
23119             
23120             if(this.labelmd > 0){
23121                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23122                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23123             }
23124             
23125             if(this.labelsm > 0){
23126                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23127                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23128             }
23129             
23130             if(this.labelxs > 0){
23131                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23132                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23133             }
23134             
23135         } else if ( this.fieldLabel.length) {
23136 //                Roo.log(" label");
23137                 cfg.cn = [
23138                    
23139                     {
23140                         tag: this.boxLabel ? 'span' : 'label',
23141                         'for': id,
23142                         cls: 'control-label box-input-label',
23143                         //cls : 'input-group-addon',
23144                         html : this.fieldLabel
23145                     },
23146                     
23147                     inputblock
23148                     
23149                 ];
23150                 if (boxLabelCfg) {
23151                     cfg.cn.push(boxLabelCfg);
23152                 }
23153
23154         } else {
23155             
23156 //                Roo.log(" no label && no align");
23157                 cfg.cn = [  inputblock ] ;
23158                 if (boxLabelCfg) {
23159                     cfg.cn.push(boxLabelCfg);
23160                 }
23161
23162                 
23163         }
23164         
23165        
23166         
23167         if(this.inputType != 'radio'){
23168             cfg.cn.push(hidden);
23169         }
23170         
23171         return cfg;
23172         
23173     },
23174     
23175     /**
23176      * return the real input element.
23177      */
23178     inputEl: function ()
23179     {
23180         return this.el.select('input.roo-' + this.inputType,true).first();
23181     },
23182     hiddenEl: function ()
23183     {
23184         return this.el.select('input.roo-hidden-value',true).first();
23185     },
23186     
23187     labelEl: function()
23188     {
23189         return this.el.select('label.control-label',true).first();
23190     },
23191     /* depricated... */
23192     
23193     label: function()
23194     {
23195         return this.labelEl();
23196     },
23197     
23198     boxLabelEl: function()
23199     {
23200         return this.el.select('label.box-label',true).first();
23201     },
23202     
23203     initEvents : function()
23204     {
23205 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23206         
23207         this.inputEl().on('click', this.onClick,  this);
23208         
23209         if (this.boxLabel) { 
23210             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23211         }
23212         
23213         this.startValue = this.getValue();
23214         
23215         if(this.groupId){
23216             Roo.bootstrap.CheckBox.register(this);
23217         }
23218     },
23219     
23220     onClick : function(e)
23221     {   
23222         if(this.fireEvent('click', this, e) !== false){
23223             this.setChecked(!this.checked);
23224         }
23225         
23226     },
23227     
23228     setChecked : function(state,suppressEvent)
23229     {
23230         this.startValue = this.getValue();
23231
23232         if(this.inputType == 'radio'){
23233             
23234             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23235                 e.dom.checked = false;
23236             });
23237             
23238             this.inputEl().dom.checked = true;
23239             
23240             this.inputEl().dom.value = this.inputValue;
23241             
23242             if(suppressEvent !== true){
23243                 this.fireEvent('check', this, true);
23244             }
23245             
23246             this.validate();
23247             
23248             return;
23249         }
23250         
23251         this.checked = state;
23252         
23253         this.inputEl().dom.checked = state;
23254         
23255         
23256         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23257         
23258         if(suppressEvent !== true){
23259             this.fireEvent('check', this, state);
23260         }
23261         
23262         this.validate();
23263     },
23264     
23265     getValue : function()
23266     {
23267         if(this.inputType == 'radio'){
23268             return this.getGroupValue();
23269         }
23270         
23271         return this.hiddenEl().dom.value;
23272         
23273     },
23274     
23275     getGroupValue : function()
23276     {
23277         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23278             return '';
23279         }
23280         
23281         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23282     },
23283     
23284     setValue : function(v,suppressEvent)
23285     {
23286         if(this.inputType == 'radio'){
23287             this.setGroupValue(v, suppressEvent);
23288             return;
23289         }
23290         
23291         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23292         
23293         this.validate();
23294     },
23295     
23296     setGroupValue : function(v, suppressEvent)
23297     {
23298         this.startValue = this.getValue();
23299         
23300         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23301             e.dom.checked = false;
23302             
23303             if(e.dom.value == v){
23304                 e.dom.checked = true;
23305             }
23306         });
23307         
23308         if(suppressEvent !== true){
23309             this.fireEvent('check', this, true);
23310         }
23311
23312         this.validate();
23313         
23314         return;
23315     },
23316     
23317     validate : function()
23318     {
23319         if(this.getVisibilityEl().hasClass('hidden')){
23320             return true;
23321         }
23322         
23323         if(
23324                 this.disabled || 
23325                 (this.inputType == 'radio' && this.validateRadio()) ||
23326                 (this.inputType == 'checkbox' && this.validateCheckbox())
23327         ){
23328             this.markValid();
23329             return true;
23330         }
23331         
23332         this.markInvalid();
23333         return false;
23334     },
23335     
23336     validateRadio : function()
23337     {
23338         if(this.getVisibilityEl().hasClass('hidden')){
23339             return true;
23340         }
23341         
23342         if(this.allowBlank){
23343             return true;
23344         }
23345         
23346         var valid = false;
23347         
23348         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23349             if(!e.dom.checked){
23350                 return;
23351             }
23352             
23353             valid = true;
23354             
23355             return false;
23356         });
23357         
23358         return valid;
23359     },
23360     
23361     validateCheckbox : function()
23362     {
23363         if(!this.groupId){
23364             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23365             //return (this.getValue() == this.inputValue) ? true : false;
23366         }
23367         
23368         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23369         
23370         if(!group){
23371             return false;
23372         }
23373         
23374         var r = false;
23375         
23376         for(var i in group){
23377             if(group[i].el.isVisible(true)){
23378                 r = false;
23379                 break;
23380             }
23381             
23382             r = true;
23383         }
23384         
23385         for(var i in group){
23386             if(r){
23387                 break;
23388             }
23389             
23390             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23391         }
23392         
23393         return r;
23394     },
23395     
23396     /**
23397      * Mark this field as valid
23398      */
23399     markValid : function()
23400     {
23401         var _this = this;
23402         
23403         this.fireEvent('valid', this);
23404         
23405         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23406         
23407         if(this.groupId){
23408             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23409         }
23410         
23411         if(label){
23412             label.markValid();
23413         }
23414
23415         if(this.inputType == 'radio'){
23416             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23417                 var fg = e.findParent('.form-group', false, true);
23418                 if (Roo.bootstrap.version == 3) {
23419                     fg.removeClass([_this.invalidClass, _this.validClass]);
23420                     fg.addClass(_this.validClass);
23421                 } else {
23422                     fg.removeClass(['is-valid', 'is-invalid']);
23423                     fg.addClass('is-valid');
23424                 }
23425             });
23426             
23427             return;
23428         }
23429
23430         if(!this.groupId){
23431             var fg = this.el.findParent('.form-group', false, true);
23432             if (Roo.bootstrap.version == 3) {
23433                 fg.removeClass([this.invalidClass, this.validClass]);
23434                 fg.addClass(this.validClass);
23435             } else {
23436                 fg.removeClass(['is-valid', 'is-invalid']);
23437                 fg.addClass('is-valid');
23438             }
23439             return;
23440         }
23441         
23442         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23443         
23444         if(!group){
23445             return;
23446         }
23447         
23448         for(var i in group){
23449             var fg = group[i].el.findParent('.form-group', false, true);
23450             if (Roo.bootstrap.version == 3) {
23451                 fg.removeClass([this.invalidClass, this.validClass]);
23452                 fg.addClass(this.validClass);
23453             } else {
23454                 fg.removeClass(['is-valid', 'is-invalid']);
23455                 fg.addClass('is-valid');
23456             }
23457         }
23458     },
23459     
23460      /**
23461      * Mark this field as invalid
23462      * @param {String} msg The validation message
23463      */
23464     markInvalid : function(msg)
23465     {
23466         if(this.allowBlank){
23467             return;
23468         }
23469         
23470         var _this = this;
23471         
23472         this.fireEvent('invalid', this, msg);
23473         
23474         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23475         
23476         if(this.groupId){
23477             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23478         }
23479         
23480         if(label){
23481             label.markInvalid();
23482         }
23483             
23484         if(this.inputType == 'radio'){
23485             
23486             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23487                 var fg = e.findParent('.form-group', false, true);
23488                 if (Roo.bootstrap.version == 3) {
23489                     fg.removeClass([_this.invalidClass, _this.validClass]);
23490                     fg.addClass(_this.invalidClass);
23491                 } else {
23492                     fg.removeClass(['is-invalid', 'is-valid']);
23493                     fg.addClass('is-invalid');
23494                 }
23495             });
23496             
23497             return;
23498         }
23499         
23500         if(!this.groupId){
23501             var fg = this.el.findParent('.form-group', false, true);
23502             if (Roo.bootstrap.version == 3) {
23503                 fg.removeClass([_this.invalidClass, _this.validClass]);
23504                 fg.addClass(_this.invalidClass);
23505             } else {
23506                 fg.removeClass(['is-invalid', 'is-valid']);
23507                 fg.addClass('is-invalid');
23508             }
23509             return;
23510         }
23511         
23512         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23513         
23514         if(!group){
23515             return;
23516         }
23517         
23518         for(var i in group){
23519             var fg = group[i].el.findParent('.form-group', false, true);
23520             if (Roo.bootstrap.version == 3) {
23521                 fg.removeClass([_this.invalidClass, _this.validClass]);
23522                 fg.addClass(_this.invalidClass);
23523             } else {
23524                 fg.removeClass(['is-invalid', 'is-valid']);
23525                 fg.addClass('is-invalid');
23526             }
23527         }
23528         
23529     },
23530     
23531     clearInvalid : function()
23532     {
23533         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23534         
23535         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23536         
23537         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23538         
23539         if (label && label.iconEl) {
23540             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23541             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23542         }
23543     },
23544     
23545     disable : function()
23546     {
23547         if(this.inputType != 'radio'){
23548             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23549             return;
23550         }
23551         
23552         var _this = this;
23553         
23554         if(this.rendered){
23555             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23556                 _this.getActionEl().addClass(this.disabledClass);
23557                 e.dom.disabled = true;
23558             });
23559         }
23560         
23561         this.disabled = true;
23562         this.fireEvent("disable", this);
23563         return this;
23564     },
23565
23566     enable : function()
23567     {
23568         if(this.inputType != 'radio'){
23569             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23570             return;
23571         }
23572         
23573         var _this = this;
23574         
23575         if(this.rendered){
23576             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23577                 _this.getActionEl().removeClass(this.disabledClass);
23578                 e.dom.disabled = false;
23579             });
23580         }
23581         
23582         this.disabled = false;
23583         this.fireEvent("enable", this);
23584         return this;
23585     },
23586     
23587     setBoxLabel : function(v)
23588     {
23589         this.boxLabel = v;
23590         
23591         if(this.rendered){
23592             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23593         }
23594     }
23595
23596 });
23597
23598 Roo.apply(Roo.bootstrap.CheckBox, {
23599     
23600     groups: {},
23601     
23602      /**
23603     * register a CheckBox Group
23604     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23605     */
23606     register : function(checkbox)
23607     {
23608         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23609             this.groups[checkbox.groupId] = {};
23610         }
23611         
23612         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23613             return;
23614         }
23615         
23616         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23617         
23618     },
23619     /**
23620     * fetch a CheckBox Group based on the group ID
23621     * @param {string} the group ID
23622     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23623     */
23624     get: function(groupId) {
23625         if (typeof(this.groups[groupId]) == 'undefined') {
23626             return false;
23627         }
23628         
23629         return this.groups[groupId] ;
23630     }
23631     
23632     
23633 });
23634 /*
23635  * - LGPL
23636  *
23637  * RadioItem
23638  * 
23639  */
23640
23641 /**
23642  * @class Roo.bootstrap.Radio
23643  * @extends Roo.bootstrap.Component
23644  * Bootstrap Radio class
23645  * @cfg {String} boxLabel - the label associated
23646  * @cfg {String} value - the value of radio
23647  * 
23648  * @constructor
23649  * Create a new Radio
23650  * @param {Object} config The config object
23651  */
23652 Roo.bootstrap.Radio = function(config){
23653     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23654     
23655 };
23656
23657 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23658     
23659     boxLabel : '',
23660     
23661     value : '',
23662     
23663     getAutoCreate : function()
23664     {
23665         var cfg = {
23666             tag : 'div',
23667             cls : 'form-group radio',
23668             cn : [
23669                 {
23670                     tag : 'label',
23671                     cls : 'box-label',
23672                     html : this.boxLabel
23673                 }
23674             ]
23675         };
23676         
23677         return cfg;
23678     },
23679     
23680     initEvents : function() 
23681     {
23682         this.parent().register(this);
23683         
23684         this.el.on('click', this.onClick, this);
23685         
23686     },
23687     
23688     onClick : function(e)
23689     {
23690         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23691             this.setChecked(true);
23692         }
23693     },
23694     
23695     setChecked : function(state, suppressEvent)
23696     {
23697         this.parent().setValue(this.value, suppressEvent);
23698         
23699     },
23700     
23701     setBoxLabel : function(v)
23702     {
23703         this.boxLabel = v;
23704         
23705         if(this.rendered){
23706             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23707         }
23708     }
23709     
23710 });
23711  
23712
23713  /*
23714  * - LGPL
23715  *
23716  * Input
23717  * 
23718  */
23719
23720 /**
23721  * @class Roo.bootstrap.SecurePass
23722  * @extends Roo.bootstrap.Input
23723  * Bootstrap SecurePass class
23724  *
23725  * 
23726  * @constructor
23727  * Create a new SecurePass
23728  * @param {Object} config The config object
23729  */
23730  
23731 Roo.bootstrap.SecurePass = function (config) {
23732     // these go here, so the translation tool can replace them..
23733     this.errors = {
23734         PwdEmpty: "Please type a password, and then retype it to confirm.",
23735         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23736         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23737         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23738         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23739         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23740         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23741         TooWeak: "Your password is Too Weak."
23742     },
23743     this.meterLabel = "Password strength:";
23744     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23745     this.meterClass = [
23746         "roo-password-meter-tooweak", 
23747         "roo-password-meter-weak", 
23748         "roo-password-meter-medium", 
23749         "roo-password-meter-strong", 
23750         "roo-password-meter-grey"
23751     ];
23752     
23753     this.errors = {};
23754     
23755     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23756 }
23757
23758 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23759     /**
23760      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23761      * {
23762      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23763      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23764      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23765      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23766      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23767      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23768      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23769      * })
23770      */
23771     // private
23772     
23773     meterWidth: 300,
23774     errorMsg :'',    
23775     errors: false,
23776     imageRoot: '/',
23777     /**
23778      * @cfg {String/Object} Label for the strength meter (defaults to
23779      * 'Password strength:')
23780      */
23781     // private
23782     meterLabel: '',
23783     /**
23784      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23785      * ['Weak', 'Medium', 'Strong'])
23786      */
23787     // private    
23788     pwdStrengths: false,    
23789     // private
23790     strength: 0,
23791     // private
23792     _lastPwd: null,
23793     // private
23794     kCapitalLetter: 0,
23795     kSmallLetter: 1,
23796     kDigit: 2,
23797     kPunctuation: 3,
23798     
23799     insecure: false,
23800     // private
23801     initEvents: function ()
23802     {
23803         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23804
23805         if (this.el.is('input[type=password]') && Roo.isSafari) {
23806             this.el.on('keydown', this.SafariOnKeyDown, this);
23807         }
23808
23809         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23810     },
23811     // private
23812     onRender: function (ct, position)
23813     {
23814         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23815         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23816         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23817
23818         this.trigger.createChild({
23819                    cn: [
23820                     {
23821                     //id: 'PwdMeter',
23822                     tag: 'div',
23823                     cls: 'roo-password-meter-grey col-xs-12',
23824                     style: {
23825                         //width: 0,
23826                         //width: this.meterWidth + 'px'                                                
23827                         }
23828                     },
23829                     {                            
23830                          cls: 'roo-password-meter-text'                          
23831                     }
23832                 ]            
23833         });
23834
23835          
23836         if (this.hideTrigger) {
23837             this.trigger.setDisplayed(false);
23838         }
23839         this.setSize(this.width || '', this.height || '');
23840     },
23841     // private
23842     onDestroy: function ()
23843     {
23844         if (this.trigger) {
23845             this.trigger.removeAllListeners();
23846             this.trigger.remove();
23847         }
23848         if (this.wrap) {
23849             this.wrap.remove();
23850         }
23851         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23852     },
23853     // private
23854     checkStrength: function ()
23855     {
23856         var pwd = this.inputEl().getValue();
23857         if (pwd == this._lastPwd) {
23858             return;
23859         }
23860
23861         var strength;
23862         if (this.ClientSideStrongPassword(pwd)) {
23863             strength = 3;
23864         } else if (this.ClientSideMediumPassword(pwd)) {
23865             strength = 2;
23866         } else if (this.ClientSideWeakPassword(pwd)) {
23867             strength = 1;
23868         } else {
23869             strength = 0;
23870         }
23871         
23872         Roo.log('strength1: ' + strength);
23873         
23874         //var pm = this.trigger.child('div/div/div').dom;
23875         var pm = this.trigger.child('div/div');
23876         pm.removeClass(this.meterClass);
23877         pm.addClass(this.meterClass[strength]);
23878                 
23879         
23880         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23881                 
23882         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23883         
23884         this._lastPwd = pwd;
23885     },
23886     reset: function ()
23887     {
23888         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23889         
23890         this._lastPwd = '';
23891         
23892         var pm = this.trigger.child('div/div');
23893         pm.removeClass(this.meterClass);
23894         pm.addClass('roo-password-meter-grey');        
23895         
23896         
23897         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23898         
23899         pt.innerHTML = '';
23900         this.inputEl().dom.type='password';
23901     },
23902     // private
23903     validateValue: function (value)
23904     {
23905         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23906             return false;
23907         }
23908         if (value.length == 0) {
23909             if (this.allowBlank) {
23910                 this.clearInvalid();
23911                 return true;
23912             }
23913
23914             this.markInvalid(this.errors.PwdEmpty);
23915             this.errorMsg = this.errors.PwdEmpty;
23916             return false;
23917         }
23918         
23919         if(this.insecure){
23920             return true;
23921         }
23922         
23923         if (!value.match(/[\x21-\x7e]+/)) {
23924             this.markInvalid(this.errors.PwdBadChar);
23925             this.errorMsg = this.errors.PwdBadChar;
23926             return false;
23927         }
23928         if (value.length < 6) {
23929             this.markInvalid(this.errors.PwdShort);
23930             this.errorMsg = this.errors.PwdShort;
23931             return false;
23932         }
23933         if (value.length > 16) {
23934             this.markInvalid(this.errors.PwdLong);
23935             this.errorMsg = this.errors.PwdLong;
23936             return false;
23937         }
23938         var strength;
23939         if (this.ClientSideStrongPassword(value)) {
23940             strength = 3;
23941         } else if (this.ClientSideMediumPassword(value)) {
23942             strength = 2;
23943         } else if (this.ClientSideWeakPassword(value)) {
23944             strength = 1;
23945         } else {
23946             strength = 0;
23947         }
23948
23949         
23950         if (strength < 2) {
23951             //this.markInvalid(this.errors.TooWeak);
23952             this.errorMsg = this.errors.TooWeak;
23953             //return false;
23954         }
23955         
23956         
23957         console.log('strength2: ' + strength);
23958         
23959         //var pm = this.trigger.child('div/div/div').dom;
23960         
23961         var pm = this.trigger.child('div/div');
23962         pm.removeClass(this.meterClass);
23963         pm.addClass(this.meterClass[strength]);
23964                 
23965         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23966                 
23967         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23968         
23969         this.errorMsg = ''; 
23970         return true;
23971     },
23972     // private
23973     CharacterSetChecks: function (type)
23974     {
23975         this.type = type;
23976         this.fResult = false;
23977     },
23978     // private
23979     isctype: function (character, type)
23980     {
23981         switch (type) {  
23982             case this.kCapitalLetter:
23983                 if (character >= 'A' && character <= 'Z') {
23984                     return true;
23985                 }
23986                 break;
23987             
23988             case this.kSmallLetter:
23989                 if (character >= 'a' && character <= 'z') {
23990                     return true;
23991                 }
23992                 break;
23993             
23994             case this.kDigit:
23995                 if (character >= '0' && character <= '9') {
23996                     return true;
23997                 }
23998                 break;
23999             
24000             case this.kPunctuation:
24001                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24002                     return true;
24003                 }
24004                 break;
24005             
24006             default:
24007                 return false;
24008         }
24009
24010     },
24011     // private
24012     IsLongEnough: function (pwd, size)
24013     {
24014         return !(pwd == null || isNaN(size) || pwd.length < size);
24015     },
24016     // private
24017     SpansEnoughCharacterSets: function (word, nb)
24018     {
24019         if (!this.IsLongEnough(word, nb))
24020         {
24021             return false;
24022         }
24023
24024         var characterSetChecks = new Array(
24025             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24026             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24027         );
24028         
24029         for (var index = 0; index < word.length; ++index) {
24030             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24031                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24032                     characterSetChecks[nCharSet].fResult = true;
24033                     break;
24034                 }
24035             }
24036         }
24037
24038         var nCharSets = 0;
24039         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24040             if (characterSetChecks[nCharSet].fResult) {
24041                 ++nCharSets;
24042             }
24043         }
24044
24045         if (nCharSets < nb) {
24046             return false;
24047         }
24048         return true;
24049     },
24050     // private
24051     ClientSideStrongPassword: function (pwd)
24052     {
24053         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24054     },
24055     // private
24056     ClientSideMediumPassword: function (pwd)
24057     {
24058         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24059     },
24060     // private
24061     ClientSideWeakPassword: function (pwd)
24062     {
24063         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24064     }
24065           
24066 })//<script type="text/javascript">
24067
24068 /*
24069  * Based  Ext JS Library 1.1.1
24070  * Copyright(c) 2006-2007, Ext JS, LLC.
24071  * LGPL
24072  *
24073  */
24074  
24075 /**
24076  * @class Roo.HtmlEditorCore
24077  * @extends Roo.Component
24078  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24079  *
24080  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24081  */
24082
24083 Roo.HtmlEditorCore = function(config){
24084     
24085     
24086     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24087     
24088     
24089     this.addEvents({
24090         /**
24091          * @event initialize
24092          * Fires when the editor is fully initialized (including the iframe)
24093          * @param {Roo.HtmlEditorCore} this
24094          */
24095         initialize: true,
24096         /**
24097          * @event activate
24098          * Fires when the editor is first receives the focus. Any insertion must wait
24099          * until after this event.
24100          * @param {Roo.HtmlEditorCore} this
24101          */
24102         activate: true,
24103          /**
24104          * @event beforesync
24105          * Fires before the textarea is updated with content from the editor iframe. Return false
24106          * to cancel the sync.
24107          * @param {Roo.HtmlEditorCore} this
24108          * @param {String} html
24109          */
24110         beforesync: true,
24111          /**
24112          * @event beforepush
24113          * Fires before the iframe editor is updated with content from the textarea. Return false
24114          * to cancel the push.
24115          * @param {Roo.HtmlEditorCore} this
24116          * @param {String} html
24117          */
24118         beforepush: true,
24119          /**
24120          * @event sync
24121          * Fires when the textarea is updated with content from the editor iframe.
24122          * @param {Roo.HtmlEditorCore} this
24123          * @param {String} html
24124          */
24125         sync: true,
24126          /**
24127          * @event push
24128          * Fires when the iframe editor is updated with content from the textarea.
24129          * @param {Roo.HtmlEditorCore} this
24130          * @param {String} html
24131          */
24132         push: true,
24133         
24134         /**
24135          * @event editorevent
24136          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24137          * @param {Roo.HtmlEditorCore} this
24138          */
24139         editorevent: true
24140         
24141     });
24142     
24143     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24144     
24145     // defaults : white / black...
24146     this.applyBlacklists();
24147     
24148     
24149     
24150 };
24151
24152
24153 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24154
24155
24156      /**
24157      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24158      */
24159     
24160     owner : false,
24161     
24162      /**
24163      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24164      *                        Roo.resizable.
24165      */
24166     resizable : false,
24167      /**
24168      * @cfg {Number} height (in pixels)
24169      */   
24170     height: 300,
24171    /**
24172      * @cfg {Number} width (in pixels)
24173      */   
24174     width: 500,
24175     
24176     /**
24177      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24178      * 
24179      */
24180     stylesheets: false,
24181     
24182     // id of frame..
24183     frameId: false,
24184     
24185     // private properties
24186     validationEvent : false,
24187     deferHeight: true,
24188     initialized : false,
24189     activated : false,
24190     sourceEditMode : false,
24191     onFocus : Roo.emptyFn,
24192     iframePad:3,
24193     hideMode:'offsets',
24194     
24195     clearUp: true,
24196     
24197     // blacklist + whitelisted elements..
24198     black: false,
24199     white: false,
24200      
24201     bodyCls : '',
24202
24203     /**
24204      * Protected method that will not generally be called directly. It
24205      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24206      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24207      */
24208     getDocMarkup : function(){
24209         // body styles..
24210         var st = '';
24211         
24212         // inherit styels from page...?? 
24213         if (this.stylesheets === false) {
24214             
24215             Roo.get(document.head).select('style').each(function(node) {
24216                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24217             });
24218             
24219             Roo.get(document.head).select('link').each(function(node) { 
24220                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24221             });
24222             
24223         } else if (!this.stylesheets.length) {
24224                 // simple..
24225                 st = '<style type="text/css">' +
24226                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24227                    '</style>';
24228         } else {
24229             for (var i in this.stylesheets) { 
24230                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24231             }
24232             
24233         }
24234         
24235         st +=  '<style type="text/css">' +
24236             'IMG { cursor: pointer } ' +
24237         '</style>';
24238
24239         var cls = 'roo-htmleditor-body';
24240         
24241         if(this.bodyCls.length){
24242             cls += ' ' + this.bodyCls;
24243         }
24244         
24245         return '<html><head>' + st  +
24246             //<style type="text/css">' +
24247             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24248             //'</style>' +
24249             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24250     },
24251
24252     // private
24253     onRender : function(ct, position)
24254     {
24255         var _t = this;
24256         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24257         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24258         
24259         
24260         this.el.dom.style.border = '0 none';
24261         this.el.dom.setAttribute('tabIndex', -1);
24262         this.el.addClass('x-hidden hide');
24263         
24264         
24265         
24266         if(Roo.isIE){ // fix IE 1px bogus margin
24267             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24268         }
24269        
24270         
24271         this.frameId = Roo.id();
24272         
24273          
24274         
24275         var iframe = this.owner.wrap.createChild({
24276             tag: 'iframe',
24277             cls: 'form-control', // bootstrap..
24278             id: this.frameId,
24279             name: this.frameId,
24280             frameBorder : 'no',
24281             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24282         }, this.el
24283         );
24284         
24285         
24286         this.iframe = iframe.dom;
24287
24288          this.assignDocWin();
24289         
24290         this.doc.designMode = 'on';
24291        
24292         this.doc.open();
24293         this.doc.write(this.getDocMarkup());
24294         this.doc.close();
24295
24296         
24297         var task = { // must defer to wait for browser to be ready
24298             run : function(){
24299                 //console.log("run task?" + this.doc.readyState);
24300                 this.assignDocWin();
24301                 if(this.doc.body || this.doc.readyState == 'complete'){
24302                     try {
24303                         this.doc.designMode="on";
24304                     } catch (e) {
24305                         return;
24306                     }
24307                     Roo.TaskMgr.stop(task);
24308                     this.initEditor.defer(10, this);
24309                 }
24310             },
24311             interval : 10,
24312             duration: 10000,
24313             scope: this
24314         };
24315         Roo.TaskMgr.start(task);
24316
24317     },
24318
24319     // private
24320     onResize : function(w, h)
24321     {
24322          Roo.log('resize: ' +w + ',' + h );
24323         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24324         if(!this.iframe){
24325             return;
24326         }
24327         if(typeof w == 'number'){
24328             
24329             this.iframe.style.width = w + 'px';
24330         }
24331         if(typeof h == 'number'){
24332             
24333             this.iframe.style.height = h + 'px';
24334             if(this.doc){
24335                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24336             }
24337         }
24338         
24339     },
24340
24341     /**
24342      * Toggles the editor between standard and source edit mode.
24343      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24344      */
24345     toggleSourceEdit : function(sourceEditMode){
24346         
24347         this.sourceEditMode = sourceEditMode === true;
24348         
24349         if(this.sourceEditMode){
24350  
24351             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24352             
24353         }else{
24354             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24355             //this.iframe.className = '';
24356             this.deferFocus();
24357         }
24358         //this.setSize(this.owner.wrap.getSize());
24359         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24360     },
24361
24362     
24363   
24364
24365     /**
24366      * Protected method that will not generally be called directly. If you need/want
24367      * custom HTML cleanup, this is the method you should override.
24368      * @param {String} html The HTML to be cleaned
24369      * return {String} The cleaned HTML
24370      */
24371     cleanHtml : function(html){
24372         html = String(html);
24373         if(html.length > 5){
24374             if(Roo.isSafari){ // strip safari nonsense
24375                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24376             }
24377         }
24378         if(html == '&nbsp;'){
24379             html = '';
24380         }
24381         return html;
24382     },
24383
24384     /**
24385      * HTML Editor -> Textarea
24386      * Protected method that will not generally be called directly. Syncs the contents
24387      * of the editor iframe with the textarea.
24388      */
24389     syncValue : function(){
24390         if(this.initialized){
24391             var bd = (this.doc.body || this.doc.documentElement);
24392             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24393             var html = bd.innerHTML;
24394             if(Roo.isSafari){
24395                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24396                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24397                 if(m && m[1]){
24398                     html = '<div style="'+m[0]+'">' + html + '</div>';
24399                 }
24400             }
24401             html = this.cleanHtml(html);
24402             // fix up the special chars.. normaly like back quotes in word...
24403             // however we do not want to do this with chinese..
24404             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24405                 
24406                 var cc = match.charCodeAt();
24407
24408                 // Get the character value, handling surrogate pairs
24409                 if (match.length == 2) {
24410                     // It's a surrogate pair, calculate the Unicode code point
24411                     var high = match.charCodeAt(0) - 0xD800;
24412                     var low  = match.charCodeAt(1) - 0xDC00;
24413                     cc = (high * 0x400) + low + 0x10000;
24414                 }  else if (
24415                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24416                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24417                     (cc >= 0xf900 && cc < 0xfb00 )
24418                 ) {
24419                         return match;
24420                 }  
24421          
24422                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24423                 return "&#" + cc + ";";
24424                 
24425                 
24426             });
24427             
24428             
24429              
24430             if(this.owner.fireEvent('beforesync', this, html) !== false){
24431                 this.el.dom.value = html;
24432                 this.owner.fireEvent('sync', this, html);
24433             }
24434         }
24435     },
24436
24437     /**
24438      * Protected method that will not generally be called directly. Pushes the value of the textarea
24439      * into the iframe editor.
24440      */
24441     pushValue : function(){
24442         if(this.initialized){
24443             var v = this.el.dom.value.trim();
24444             
24445 //            if(v.length < 1){
24446 //                v = '&#160;';
24447 //            }
24448             
24449             if(this.owner.fireEvent('beforepush', this, v) !== false){
24450                 var d = (this.doc.body || this.doc.documentElement);
24451                 d.innerHTML = v;
24452                 this.cleanUpPaste();
24453                 this.el.dom.value = d.innerHTML;
24454                 this.owner.fireEvent('push', this, v);
24455             }
24456         }
24457     },
24458
24459     // private
24460     deferFocus : function(){
24461         this.focus.defer(10, this);
24462     },
24463
24464     // doc'ed in Field
24465     focus : function(){
24466         if(this.win && !this.sourceEditMode){
24467             this.win.focus();
24468         }else{
24469             this.el.focus();
24470         }
24471     },
24472     
24473     assignDocWin: function()
24474     {
24475         var iframe = this.iframe;
24476         
24477          if(Roo.isIE){
24478             this.doc = iframe.contentWindow.document;
24479             this.win = iframe.contentWindow;
24480         } else {
24481 //            if (!Roo.get(this.frameId)) {
24482 //                return;
24483 //            }
24484 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24485 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24486             
24487             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24488                 return;
24489             }
24490             
24491             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24492             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24493         }
24494     },
24495     
24496     // private
24497     initEditor : function(){
24498         //console.log("INIT EDITOR");
24499         this.assignDocWin();
24500         
24501         
24502         
24503         this.doc.designMode="on";
24504         this.doc.open();
24505         this.doc.write(this.getDocMarkup());
24506         this.doc.close();
24507         
24508         var dbody = (this.doc.body || this.doc.documentElement);
24509         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24510         // this copies styles from the containing element into thsi one..
24511         // not sure why we need all of this..
24512         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24513         
24514         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24515         //ss['background-attachment'] = 'fixed'; // w3c
24516         dbody.bgProperties = 'fixed'; // ie
24517         //Roo.DomHelper.applyStyles(dbody, ss);
24518         Roo.EventManager.on(this.doc, {
24519             //'mousedown': this.onEditorEvent,
24520             'mouseup': this.onEditorEvent,
24521             'dblclick': this.onEditorEvent,
24522             'click': this.onEditorEvent,
24523             'keyup': this.onEditorEvent,
24524             buffer:100,
24525             scope: this
24526         });
24527         if(Roo.isGecko){
24528             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24529         }
24530         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24531             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24532         }
24533         this.initialized = true;
24534
24535         this.owner.fireEvent('initialize', this);
24536         this.pushValue();
24537     },
24538
24539     // private
24540     onDestroy : function(){
24541         
24542         
24543         
24544         if(this.rendered){
24545             
24546             //for (var i =0; i < this.toolbars.length;i++) {
24547             //    // fixme - ask toolbars for heights?
24548             //    this.toolbars[i].onDestroy();
24549            // }
24550             
24551             //this.wrap.dom.innerHTML = '';
24552             //this.wrap.remove();
24553         }
24554     },
24555
24556     // private
24557     onFirstFocus : function(){
24558         
24559         this.assignDocWin();
24560         
24561         
24562         this.activated = true;
24563          
24564     
24565         if(Roo.isGecko){ // prevent silly gecko errors
24566             this.win.focus();
24567             var s = this.win.getSelection();
24568             if(!s.focusNode || s.focusNode.nodeType != 3){
24569                 var r = s.getRangeAt(0);
24570                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24571                 r.collapse(true);
24572                 this.deferFocus();
24573             }
24574             try{
24575                 this.execCmd('useCSS', true);
24576                 this.execCmd('styleWithCSS', false);
24577             }catch(e){}
24578         }
24579         this.owner.fireEvent('activate', this);
24580     },
24581
24582     // private
24583     adjustFont: function(btn){
24584         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24585         //if(Roo.isSafari){ // safari
24586         //    adjust *= 2;
24587        // }
24588         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24589         if(Roo.isSafari){ // safari
24590             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24591             v =  (v < 10) ? 10 : v;
24592             v =  (v > 48) ? 48 : v;
24593             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24594             
24595         }
24596         
24597         
24598         v = Math.max(1, v+adjust);
24599         
24600         this.execCmd('FontSize', v  );
24601     },
24602
24603     onEditorEvent : function(e)
24604     {
24605         this.owner.fireEvent('editorevent', this, e);
24606       //  this.updateToolbar();
24607         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24608     },
24609
24610     insertTag : function(tg)
24611     {
24612         // could be a bit smarter... -> wrap the current selected tRoo..
24613         if (tg.toLowerCase() == 'span' ||
24614             tg.toLowerCase() == 'code' ||
24615             tg.toLowerCase() == 'sup' ||
24616             tg.toLowerCase() == 'sub' 
24617             ) {
24618             
24619             range = this.createRange(this.getSelection());
24620             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24621             wrappingNode.appendChild(range.extractContents());
24622             range.insertNode(wrappingNode);
24623
24624             return;
24625             
24626             
24627             
24628         }
24629         this.execCmd("formatblock",   tg);
24630         
24631     },
24632     
24633     insertText : function(txt)
24634     {
24635         
24636         
24637         var range = this.createRange();
24638         range.deleteContents();
24639                //alert(Sender.getAttribute('label'));
24640                
24641         range.insertNode(this.doc.createTextNode(txt));
24642     } ,
24643     
24644      
24645
24646     /**
24647      * Executes a Midas editor command on the editor document and performs necessary focus and
24648      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24649      * @param {String} cmd The Midas command
24650      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24651      */
24652     relayCmd : function(cmd, value){
24653         this.win.focus();
24654         this.execCmd(cmd, value);
24655         this.owner.fireEvent('editorevent', this);
24656         //this.updateToolbar();
24657         this.owner.deferFocus();
24658     },
24659
24660     /**
24661      * Executes a Midas editor command directly on the editor document.
24662      * For visual commands, you should use {@link #relayCmd} instead.
24663      * <b>This should only be called after the editor is initialized.</b>
24664      * @param {String} cmd The Midas command
24665      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24666      */
24667     execCmd : function(cmd, value){
24668         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24669         this.syncValue();
24670     },
24671  
24672  
24673    
24674     /**
24675      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24676      * to insert tRoo.
24677      * @param {String} text | dom node.. 
24678      */
24679     insertAtCursor : function(text)
24680     {
24681         
24682         if(!this.activated){
24683             return;
24684         }
24685         /*
24686         if(Roo.isIE){
24687             this.win.focus();
24688             var r = this.doc.selection.createRange();
24689             if(r){
24690                 r.collapse(true);
24691                 r.pasteHTML(text);
24692                 this.syncValue();
24693                 this.deferFocus();
24694             
24695             }
24696             return;
24697         }
24698         */
24699         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24700             this.win.focus();
24701             
24702             
24703             // from jquery ui (MIT licenced)
24704             var range, node;
24705             var win = this.win;
24706             
24707             if (win.getSelection && win.getSelection().getRangeAt) {
24708                 range = win.getSelection().getRangeAt(0);
24709                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24710                 range.insertNode(node);
24711             } else if (win.document.selection && win.document.selection.createRange) {
24712                 // no firefox support
24713                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24714                 win.document.selection.createRange().pasteHTML(txt);
24715             } else {
24716                 // no firefox support
24717                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24718                 this.execCmd('InsertHTML', txt);
24719             } 
24720             
24721             this.syncValue();
24722             
24723             this.deferFocus();
24724         }
24725     },
24726  // private
24727     mozKeyPress : function(e){
24728         if(e.ctrlKey){
24729             var c = e.getCharCode(), cmd;
24730           
24731             if(c > 0){
24732                 c = String.fromCharCode(c).toLowerCase();
24733                 switch(c){
24734                     case 'b':
24735                         cmd = 'bold';
24736                         break;
24737                     case 'i':
24738                         cmd = 'italic';
24739                         break;
24740                     
24741                     case 'u':
24742                         cmd = 'underline';
24743                         break;
24744                     
24745                     case 'v':
24746                         this.cleanUpPaste.defer(100, this);
24747                         return;
24748                         
24749                 }
24750                 if(cmd){
24751                     this.win.focus();
24752                     this.execCmd(cmd);
24753                     this.deferFocus();
24754                     e.preventDefault();
24755                 }
24756                 
24757             }
24758         }
24759     },
24760
24761     // private
24762     fixKeys : function(){ // load time branching for fastest keydown performance
24763         if(Roo.isIE){
24764             return function(e){
24765                 var k = e.getKey(), r;
24766                 if(k == e.TAB){
24767                     e.stopEvent();
24768                     r = this.doc.selection.createRange();
24769                     if(r){
24770                         r.collapse(true);
24771                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24772                         this.deferFocus();
24773                     }
24774                     return;
24775                 }
24776                 
24777                 if(k == e.ENTER){
24778                     r = this.doc.selection.createRange();
24779                     if(r){
24780                         var target = r.parentElement();
24781                         if(!target || target.tagName.toLowerCase() != 'li'){
24782                             e.stopEvent();
24783                             r.pasteHTML('<br />');
24784                             r.collapse(false);
24785                             r.select();
24786                         }
24787                     }
24788                 }
24789                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24790                     this.cleanUpPaste.defer(100, this);
24791                     return;
24792                 }
24793                 
24794                 
24795             };
24796         }else if(Roo.isOpera){
24797             return function(e){
24798                 var k = e.getKey();
24799                 if(k == e.TAB){
24800                     e.stopEvent();
24801                     this.win.focus();
24802                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24803                     this.deferFocus();
24804                 }
24805                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24806                     this.cleanUpPaste.defer(100, this);
24807                     return;
24808                 }
24809                 
24810             };
24811         }else if(Roo.isSafari){
24812             return function(e){
24813                 var k = e.getKey();
24814                 
24815                 if(k == e.TAB){
24816                     e.stopEvent();
24817                     this.execCmd('InsertText','\t');
24818                     this.deferFocus();
24819                     return;
24820                 }
24821                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24822                     this.cleanUpPaste.defer(100, this);
24823                     return;
24824                 }
24825                 
24826              };
24827         }
24828     }(),
24829     
24830     getAllAncestors: function()
24831     {
24832         var p = this.getSelectedNode();
24833         var a = [];
24834         if (!p) {
24835             a.push(p); // push blank onto stack..
24836             p = this.getParentElement();
24837         }
24838         
24839         
24840         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24841             a.push(p);
24842             p = p.parentNode;
24843         }
24844         a.push(this.doc.body);
24845         return a;
24846     },
24847     lastSel : false,
24848     lastSelNode : false,
24849     
24850     
24851     getSelection : function() 
24852     {
24853         this.assignDocWin();
24854         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24855     },
24856     
24857     getSelectedNode: function() 
24858     {
24859         // this may only work on Gecko!!!
24860         
24861         // should we cache this!!!!
24862         
24863         
24864         
24865          
24866         var range = this.createRange(this.getSelection()).cloneRange();
24867         
24868         if (Roo.isIE) {
24869             var parent = range.parentElement();
24870             while (true) {
24871                 var testRange = range.duplicate();
24872                 testRange.moveToElementText(parent);
24873                 if (testRange.inRange(range)) {
24874                     break;
24875                 }
24876                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24877                     break;
24878                 }
24879                 parent = parent.parentElement;
24880             }
24881             return parent;
24882         }
24883         
24884         // is ancestor a text element.
24885         var ac =  range.commonAncestorContainer;
24886         if (ac.nodeType == 3) {
24887             ac = ac.parentNode;
24888         }
24889         
24890         var ar = ac.childNodes;
24891          
24892         var nodes = [];
24893         var other_nodes = [];
24894         var has_other_nodes = false;
24895         for (var i=0;i<ar.length;i++) {
24896             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24897                 continue;
24898             }
24899             // fullly contained node.
24900             
24901             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24902                 nodes.push(ar[i]);
24903                 continue;
24904             }
24905             
24906             // probably selected..
24907             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24908                 other_nodes.push(ar[i]);
24909                 continue;
24910             }
24911             // outer..
24912             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24913                 continue;
24914             }
24915             
24916             
24917             has_other_nodes = true;
24918         }
24919         if (!nodes.length && other_nodes.length) {
24920             nodes= other_nodes;
24921         }
24922         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24923             return false;
24924         }
24925         
24926         return nodes[0];
24927     },
24928     createRange: function(sel)
24929     {
24930         // this has strange effects when using with 
24931         // top toolbar - not sure if it's a great idea.
24932         //this.editor.contentWindow.focus();
24933         if (typeof sel != "undefined") {
24934             try {
24935                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24936             } catch(e) {
24937                 return this.doc.createRange();
24938             }
24939         } else {
24940             return this.doc.createRange();
24941         }
24942     },
24943     getParentElement: function()
24944     {
24945         
24946         this.assignDocWin();
24947         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24948         
24949         var range = this.createRange(sel);
24950          
24951         try {
24952             var p = range.commonAncestorContainer;
24953             while (p.nodeType == 3) { // text node
24954                 p = p.parentNode;
24955             }
24956             return p;
24957         } catch (e) {
24958             return null;
24959         }
24960     
24961     },
24962     /***
24963      *
24964      * Range intersection.. the hard stuff...
24965      *  '-1' = before
24966      *  '0' = hits..
24967      *  '1' = after.
24968      *         [ -- selected range --- ]
24969      *   [fail]                        [fail]
24970      *
24971      *    basically..
24972      *      if end is before start or  hits it. fail.
24973      *      if start is after end or hits it fail.
24974      *
24975      *   if either hits (but other is outside. - then it's not 
24976      *   
24977      *    
24978      **/
24979     
24980     
24981     // @see http://www.thismuchiknow.co.uk/?p=64.
24982     rangeIntersectsNode : function(range, node)
24983     {
24984         var nodeRange = node.ownerDocument.createRange();
24985         try {
24986             nodeRange.selectNode(node);
24987         } catch (e) {
24988             nodeRange.selectNodeContents(node);
24989         }
24990     
24991         var rangeStartRange = range.cloneRange();
24992         rangeStartRange.collapse(true);
24993     
24994         var rangeEndRange = range.cloneRange();
24995         rangeEndRange.collapse(false);
24996     
24997         var nodeStartRange = nodeRange.cloneRange();
24998         nodeStartRange.collapse(true);
24999     
25000         var nodeEndRange = nodeRange.cloneRange();
25001         nodeEndRange.collapse(false);
25002     
25003         return rangeStartRange.compareBoundaryPoints(
25004                  Range.START_TO_START, nodeEndRange) == -1 &&
25005                rangeEndRange.compareBoundaryPoints(
25006                  Range.START_TO_START, nodeStartRange) == 1;
25007         
25008          
25009     },
25010     rangeCompareNode : function(range, node)
25011     {
25012         var nodeRange = node.ownerDocument.createRange();
25013         try {
25014             nodeRange.selectNode(node);
25015         } catch (e) {
25016             nodeRange.selectNodeContents(node);
25017         }
25018         
25019         
25020         range.collapse(true);
25021     
25022         nodeRange.collapse(true);
25023      
25024         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25025         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25026          
25027         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25028         
25029         var nodeIsBefore   =  ss == 1;
25030         var nodeIsAfter    = ee == -1;
25031         
25032         if (nodeIsBefore && nodeIsAfter) {
25033             return 0; // outer
25034         }
25035         if (!nodeIsBefore && nodeIsAfter) {
25036             return 1; //right trailed.
25037         }
25038         
25039         if (nodeIsBefore && !nodeIsAfter) {
25040             return 2;  // left trailed.
25041         }
25042         // fully contined.
25043         return 3;
25044     },
25045
25046     // private? - in a new class?
25047     cleanUpPaste :  function()
25048     {
25049         // cleans up the whole document..
25050         Roo.log('cleanuppaste');
25051         
25052         this.cleanUpChildren(this.doc.body);
25053         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25054         if (clean != this.doc.body.innerHTML) {
25055             this.doc.body.innerHTML = clean;
25056         }
25057         
25058     },
25059     
25060     cleanWordChars : function(input) {// change the chars to hex code
25061         var he = Roo.HtmlEditorCore;
25062         
25063         var output = input;
25064         Roo.each(he.swapCodes, function(sw) { 
25065             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25066             
25067             output = output.replace(swapper, sw[1]);
25068         });
25069         
25070         return output;
25071     },
25072     
25073     
25074     cleanUpChildren : function (n)
25075     {
25076         if (!n.childNodes.length) {
25077             return;
25078         }
25079         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25080            this.cleanUpChild(n.childNodes[i]);
25081         }
25082     },
25083     
25084     
25085         
25086     
25087     cleanUpChild : function (node)
25088     {
25089         var ed = this;
25090         //console.log(node);
25091         if (node.nodeName == "#text") {
25092             // clean up silly Windows -- stuff?
25093             return; 
25094         }
25095         if (node.nodeName == "#comment") {
25096             node.parentNode.removeChild(node);
25097             // clean up silly Windows -- stuff?
25098             return; 
25099         }
25100         var lcname = node.tagName.toLowerCase();
25101         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25102         // whitelist of tags..
25103         
25104         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25105             // remove node.
25106             node.parentNode.removeChild(node);
25107             return;
25108             
25109         }
25110         
25111         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25112         
25113         // spans with no attributes - just remove them..
25114         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25115             remove_keep_children = true;
25116         }
25117         
25118         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25119         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25120         
25121         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25122         //    remove_keep_children = true;
25123         //}
25124         
25125         if (remove_keep_children) {
25126             this.cleanUpChildren(node);
25127             // inserts everything just before this node...
25128             while (node.childNodes.length) {
25129                 var cn = node.childNodes[0];
25130                 node.removeChild(cn);
25131                 node.parentNode.insertBefore(cn, node);
25132             }
25133             node.parentNode.removeChild(node);
25134             return;
25135         }
25136         
25137         if (!node.attributes || !node.attributes.length) {
25138             
25139           
25140             
25141             
25142             this.cleanUpChildren(node);
25143             return;
25144         }
25145         
25146         function cleanAttr(n,v)
25147         {
25148             
25149             if (v.match(/^\./) || v.match(/^\//)) {
25150                 return;
25151             }
25152             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25153                 return;
25154             }
25155             if (v.match(/^#/)) {
25156                 return;
25157             }
25158             if (v.match(/^\{/)) { // allow template editing.
25159                 return;
25160             }
25161 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25162             node.removeAttribute(n);
25163             
25164         }
25165         
25166         var cwhite = this.cwhite;
25167         var cblack = this.cblack;
25168             
25169         function cleanStyle(n,v)
25170         {
25171             if (v.match(/expression/)) { //XSS?? should we even bother..
25172                 node.removeAttribute(n);
25173                 return;
25174             }
25175             
25176             var parts = v.split(/;/);
25177             var clean = [];
25178             
25179             Roo.each(parts, function(p) {
25180                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25181                 if (!p.length) {
25182                     return true;
25183                 }
25184                 var l = p.split(':').shift().replace(/\s+/g,'');
25185                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25186                 
25187                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25188 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25189                     //node.removeAttribute(n);
25190                     return true;
25191                 }
25192                 //Roo.log()
25193                 // only allow 'c whitelisted system attributes'
25194                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25195 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25196                     //node.removeAttribute(n);
25197                     return true;
25198                 }
25199                 
25200                 
25201                  
25202                 
25203                 clean.push(p);
25204                 return true;
25205             });
25206             if (clean.length) { 
25207                 node.setAttribute(n, clean.join(';'));
25208             } else {
25209                 node.removeAttribute(n);
25210             }
25211             
25212         }
25213         
25214         
25215         for (var i = node.attributes.length-1; i > -1 ; i--) {
25216             var a = node.attributes[i];
25217             //console.log(a);
25218             
25219             if (a.name.toLowerCase().substr(0,2)=='on')  {
25220                 node.removeAttribute(a.name);
25221                 continue;
25222             }
25223             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25224                 node.removeAttribute(a.name);
25225                 continue;
25226             }
25227             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25228                 cleanAttr(a.name,a.value); // fixme..
25229                 continue;
25230             }
25231             if (a.name == 'style') {
25232                 cleanStyle(a.name,a.value);
25233                 continue;
25234             }
25235             /// clean up MS crap..
25236             // tecnically this should be a list of valid class'es..
25237             
25238             
25239             if (a.name == 'class') {
25240                 if (a.value.match(/^Mso/)) {
25241                     node.removeAttribute('class');
25242                 }
25243                 
25244                 if (a.value.match(/^body$/)) {
25245                     node.removeAttribute('class');
25246                 }
25247                 continue;
25248             }
25249             
25250             // style cleanup!?
25251             // class cleanup?
25252             
25253         }
25254         
25255         
25256         this.cleanUpChildren(node);
25257         
25258         
25259     },
25260     
25261     /**
25262      * Clean up MS wordisms...
25263      */
25264     cleanWord : function(node)
25265     {
25266         if (!node) {
25267             this.cleanWord(this.doc.body);
25268             return;
25269         }
25270         
25271         if(
25272                 node.nodeName == 'SPAN' &&
25273                 !node.hasAttributes() &&
25274                 node.childNodes.length == 1 &&
25275                 node.firstChild.nodeName == "#text"  
25276         ) {
25277             var textNode = node.firstChild;
25278             node.removeChild(textNode);
25279             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25280                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25281             }
25282             node.parentNode.insertBefore(textNode, node);
25283             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25284                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25285             }
25286             node.parentNode.removeChild(node);
25287         }
25288         
25289         if (node.nodeName == "#text") {
25290             // clean up silly Windows -- stuff?
25291             return; 
25292         }
25293         if (node.nodeName == "#comment") {
25294             node.parentNode.removeChild(node);
25295             // clean up silly Windows -- stuff?
25296             return; 
25297         }
25298         
25299         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25300             node.parentNode.removeChild(node);
25301             return;
25302         }
25303         //Roo.log(node.tagName);
25304         // remove - but keep children..
25305         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25306             //Roo.log('-- removed');
25307             while (node.childNodes.length) {
25308                 var cn = node.childNodes[0];
25309                 node.removeChild(cn);
25310                 node.parentNode.insertBefore(cn, node);
25311                 // move node to parent - and clean it..
25312                 this.cleanWord(cn);
25313             }
25314             node.parentNode.removeChild(node);
25315             /// no need to iterate chidlren = it's got none..
25316             //this.iterateChildren(node, this.cleanWord);
25317             return;
25318         }
25319         // clean styles
25320         if (node.className.length) {
25321             
25322             var cn = node.className.split(/\W+/);
25323             var cna = [];
25324             Roo.each(cn, function(cls) {
25325                 if (cls.match(/Mso[a-zA-Z]+/)) {
25326                     return;
25327                 }
25328                 cna.push(cls);
25329             });
25330             node.className = cna.length ? cna.join(' ') : '';
25331             if (!cna.length) {
25332                 node.removeAttribute("class");
25333             }
25334         }
25335         
25336         if (node.hasAttribute("lang")) {
25337             node.removeAttribute("lang");
25338         }
25339         
25340         if (node.hasAttribute("style")) {
25341             
25342             var styles = node.getAttribute("style").split(";");
25343             var nstyle = [];
25344             Roo.each(styles, function(s) {
25345                 if (!s.match(/:/)) {
25346                     return;
25347                 }
25348                 var kv = s.split(":");
25349                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25350                     return;
25351                 }
25352                 // what ever is left... we allow.
25353                 nstyle.push(s);
25354             });
25355             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25356             if (!nstyle.length) {
25357                 node.removeAttribute('style');
25358             }
25359         }
25360         this.iterateChildren(node, this.cleanWord);
25361         
25362         
25363         
25364     },
25365     /**
25366      * iterateChildren of a Node, calling fn each time, using this as the scole..
25367      * @param {DomNode} node node to iterate children of.
25368      * @param {Function} fn method of this class to call on each item.
25369      */
25370     iterateChildren : function(node, fn)
25371     {
25372         if (!node.childNodes.length) {
25373                 return;
25374         }
25375         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25376            fn.call(this, node.childNodes[i])
25377         }
25378     },
25379     
25380     
25381     /**
25382      * cleanTableWidths.
25383      *
25384      * Quite often pasting from word etc.. results in tables with column and widths.
25385      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25386      *
25387      */
25388     cleanTableWidths : function(node)
25389     {
25390          
25391          
25392         if (!node) {
25393             this.cleanTableWidths(this.doc.body);
25394             return;
25395         }
25396         
25397         // ignore list...
25398         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25399             return; 
25400         }
25401         Roo.log(node.tagName);
25402         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25403             this.iterateChildren(node, this.cleanTableWidths);
25404             return;
25405         }
25406         if (node.hasAttribute('width')) {
25407             node.removeAttribute('width');
25408         }
25409         
25410          
25411         if (node.hasAttribute("style")) {
25412             // pretty basic...
25413             
25414             var styles = node.getAttribute("style").split(";");
25415             var nstyle = [];
25416             Roo.each(styles, function(s) {
25417                 if (!s.match(/:/)) {
25418                     return;
25419                 }
25420                 var kv = s.split(":");
25421                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25422                     return;
25423                 }
25424                 // what ever is left... we allow.
25425                 nstyle.push(s);
25426             });
25427             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25428             if (!nstyle.length) {
25429                 node.removeAttribute('style');
25430             }
25431         }
25432         
25433         this.iterateChildren(node, this.cleanTableWidths);
25434         
25435         
25436     },
25437     
25438     
25439     
25440     
25441     domToHTML : function(currentElement, depth, nopadtext) {
25442         
25443         depth = depth || 0;
25444         nopadtext = nopadtext || false;
25445     
25446         if (!currentElement) {
25447             return this.domToHTML(this.doc.body);
25448         }
25449         
25450         //Roo.log(currentElement);
25451         var j;
25452         var allText = false;
25453         var nodeName = currentElement.nodeName;
25454         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25455         
25456         if  (nodeName == '#text') {
25457             
25458             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25459         }
25460         
25461         
25462         var ret = '';
25463         if (nodeName != 'BODY') {
25464              
25465             var i = 0;
25466             // Prints the node tagName, such as <A>, <IMG>, etc
25467             if (tagName) {
25468                 var attr = [];
25469                 for(i = 0; i < currentElement.attributes.length;i++) {
25470                     // quoting?
25471                     var aname = currentElement.attributes.item(i).name;
25472                     if (!currentElement.attributes.item(i).value.length) {
25473                         continue;
25474                     }
25475                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25476                 }
25477                 
25478                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25479             } 
25480             else {
25481                 
25482                 // eack
25483             }
25484         } else {
25485             tagName = false;
25486         }
25487         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25488             return ret;
25489         }
25490         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25491             nopadtext = true;
25492         }
25493         
25494         
25495         // Traverse the tree
25496         i = 0;
25497         var currentElementChild = currentElement.childNodes.item(i);
25498         var allText = true;
25499         var innerHTML  = '';
25500         lastnode = '';
25501         while (currentElementChild) {
25502             // Formatting code (indent the tree so it looks nice on the screen)
25503             var nopad = nopadtext;
25504             if (lastnode == 'SPAN') {
25505                 nopad  = true;
25506             }
25507             // text
25508             if  (currentElementChild.nodeName == '#text') {
25509                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25510                 toadd = nopadtext ? toadd : toadd.trim();
25511                 if (!nopad && toadd.length > 80) {
25512                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25513                 }
25514                 innerHTML  += toadd;
25515                 
25516                 i++;
25517                 currentElementChild = currentElement.childNodes.item(i);
25518                 lastNode = '';
25519                 continue;
25520             }
25521             allText = false;
25522             
25523             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25524                 
25525             // Recursively traverse the tree structure of the child node
25526             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25527             lastnode = currentElementChild.nodeName;
25528             i++;
25529             currentElementChild=currentElement.childNodes.item(i);
25530         }
25531         
25532         ret += innerHTML;
25533         
25534         if (!allText) {
25535                 // The remaining code is mostly for formatting the tree
25536             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25537         }
25538         
25539         
25540         if (tagName) {
25541             ret+= "</"+tagName+">";
25542         }
25543         return ret;
25544         
25545     },
25546         
25547     applyBlacklists : function()
25548     {
25549         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25550         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25551         
25552         this.white = [];
25553         this.black = [];
25554         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25555             if (b.indexOf(tag) > -1) {
25556                 return;
25557             }
25558             this.white.push(tag);
25559             
25560         }, this);
25561         
25562         Roo.each(w, function(tag) {
25563             if (b.indexOf(tag) > -1) {
25564                 return;
25565             }
25566             if (this.white.indexOf(tag) > -1) {
25567                 return;
25568             }
25569             this.white.push(tag);
25570             
25571         }, this);
25572         
25573         
25574         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25575             if (w.indexOf(tag) > -1) {
25576                 return;
25577             }
25578             this.black.push(tag);
25579             
25580         }, this);
25581         
25582         Roo.each(b, function(tag) {
25583             if (w.indexOf(tag) > -1) {
25584                 return;
25585             }
25586             if (this.black.indexOf(tag) > -1) {
25587                 return;
25588             }
25589             this.black.push(tag);
25590             
25591         }, this);
25592         
25593         
25594         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25595         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25596         
25597         this.cwhite = [];
25598         this.cblack = [];
25599         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25600             if (b.indexOf(tag) > -1) {
25601                 return;
25602             }
25603             this.cwhite.push(tag);
25604             
25605         }, this);
25606         
25607         Roo.each(w, function(tag) {
25608             if (b.indexOf(tag) > -1) {
25609                 return;
25610             }
25611             if (this.cwhite.indexOf(tag) > -1) {
25612                 return;
25613             }
25614             this.cwhite.push(tag);
25615             
25616         }, this);
25617         
25618         
25619         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25620             if (w.indexOf(tag) > -1) {
25621                 return;
25622             }
25623             this.cblack.push(tag);
25624             
25625         }, this);
25626         
25627         Roo.each(b, function(tag) {
25628             if (w.indexOf(tag) > -1) {
25629                 return;
25630             }
25631             if (this.cblack.indexOf(tag) > -1) {
25632                 return;
25633             }
25634             this.cblack.push(tag);
25635             
25636         }, this);
25637     },
25638     
25639     setStylesheets : function(stylesheets)
25640     {
25641         if(typeof(stylesheets) == 'string'){
25642             Roo.get(this.iframe.contentDocument.head).createChild({
25643                 tag : 'link',
25644                 rel : 'stylesheet',
25645                 type : 'text/css',
25646                 href : stylesheets
25647             });
25648             
25649             return;
25650         }
25651         var _this = this;
25652      
25653         Roo.each(stylesheets, function(s) {
25654             if(!s.length){
25655                 return;
25656             }
25657             
25658             Roo.get(_this.iframe.contentDocument.head).createChild({
25659                 tag : 'link',
25660                 rel : 'stylesheet',
25661                 type : 'text/css',
25662                 href : s
25663             });
25664         });
25665
25666         
25667     },
25668     
25669     removeStylesheets : function()
25670     {
25671         var _this = this;
25672         
25673         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25674             s.remove();
25675         });
25676     },
25677     
25678     setStyle : function(style)
25679     {
25680         Roo.get(this.iframe.contentDocument.head).createChild({
25681             tag : 'style',
25682             type : 'text/css',
25683             html : style
25684         });
25685
25686         return;
25687     }
25688     
25689     // hide stuff that is not compatible
25690     /**
25691      * @event blur
25692      * @hide
25693      */
25694     /**
25695      * @event change
25696      * @hide
25697      */
25698     /**
25699      * @event focus
25700      * @hide
25701      */
25702     /**
25703      * @event specialkey
25704      * @hide
25705      */
25706     /**
25707      * @cfg {String} fieldClass @hide
25708      */
25709     /**
25710      * @cfg {String} focusClass @hide
25711      */
25712     /**
25713      * @cfg {String} autoCreate @hide
25714      */
25715     /**
25716      * @cfg {String} inputType @hide
25717      */
25718     /**
25719      * @cfg {String} invalidClass @hide
25720      */
25721     /**
25722      * @cfg {String} invalidText @hide
25723      */
25724     /**
25725      * @cfg {String} msgFx @hide
25726      */
25727     /**
25728      * @cfg {String} validateOnBlur @hide
25729      */
25730 });
25731
25732 Roo.HtmlEditorCore.white = [
25733         'area', 'br', 'img', 'input', 'hr', 'wbr',
25734         
25735        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25736        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25737        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25738        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25739        'table',   'ul',         'xmp', 
25740        
25741        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25742       'thead',   'tr', 
25743      
25744       'dir', 'menu', 'ol', 'ul', 'dl',
25745        
25746       'embed',  'object'
25747 ];
25748
25749
25750 Roo.HtmlEditorCore.black = [
25751     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25752         'applet', // 
25753         'base',   'basefont', 'bgsound', 'blink',  'body', 
25754         'frame',  'frameset', 'head',    'html',   'ilayer', 
25755         'iframe', 'layer',  'link',     'meta',    'object',   
25756         'script', 'style' ,'title',  'xml' // clean later..
25757 ];
25758 Roo.HtmlEditorCore.clean = [
25759     'script', 'style', 'title', 'xml'
25760 ];
25761 Roo.HtmlEditorCore.remove = [
25762     'font'
25763 ];
25764 // attributes..
25765
25766 Roo.HtmlEditorCore.ablack = [
25767     'on'
25768 ];
25769     
25770 Roo.HtmlEditorCore.aclean = [ 
25771     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25772 ];
25773
25774 // protocols..
25775 Roo.HtmlEditorCore.pwhite= [
25776         'http',  'https',  'mailto'
25777 ];
25778
25779 // white listed style attributes.
25780 Roo.HtmlEditorCore.cwhite= [
25781       //  'text-align', /// default is to allow most things..
25782       
25783          
25784 //        'font-size'//??
25785 ];
25786
25787 // black listed style attributes.
25788 Roo.HtmlEditorCore.cblack= [
25789       //  'font-size' -- this can be set by the project 
25790 ];
25791
25792
25793 Roo.HtmlEditorCore.swapCodes   =[ 
25794     [    8211, "--" ], 
25795     [    8212, "--" ], 
25796     [    8216,  "'" ],  
25797     [    8217, "'" ],  
25798     [    8220, '"' ],  
25799     [    8221, '"' ],  
25800     [    8226, "*" ],  
25801     [    8230, "..." ]
25802 ]; 
25803
25804     /*
25805  * - LGPL
25806  *
25807  * HtmlEditor
25808  * 
25809  */
25810
25811 /**
25812  * @class Roo.bootstrap.HtmlEditor
25813  * @extends Roo.bootstrap.TextArea
25814  * Bootstrap HtmlEditor class
25815
25816  * @constructor
25817  * Create a new HtmlEditor
25818  * @param {Object} config The config object
25819  */
25820
25821 Roo.bootstrap.HtmlEditor = function(config){
25822     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25823     if (!this.toolbars) {
25824         this.toolbars = [];
25825     }
25826     
25827     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25828     this.addEvents({
25829             /**
25830              * @event initialize
25831              * Fires when the editor is fully initialized (including the iframe)
25832              * @param {HtmlEditor} this
25833              */
25834             initialize: true,
25835             /**
25836              * @event activate
25837              * Fires when the editor is first receives the focus. Any insertion must wait
25838              * until after this event.
25839              * @param {HtmlEditor} this
25840              */
25841             activate: true,
25842              /**
25843              * @event beforesync
25844              * Fires before the textarea is updated with content from the editor iframe. Return false
25845              * to cancel the sync.
25846              * @param {HtmlEditor} this
25847              * @param {String} html
25848              */
25849             beforesync: true,
25850              /**
25851              * @event beforepush
25852              * Fires before the iframe editor is updated with content from the textarea. Return false
25853              * to cancel the push.
25854              * @param {HtmlEditor} this
25855              * @param {String} html
25856              */
25857             beforepush: true,
25858              /**
25859              * @event sync
25860              * Fires when the textarea is updated with content from the editor iframe.
25861              * @param {HtmlEditor} this
25862              * @param {String} html
25863              */
25864             sync: true,
25865              /**
25866              * @event push
25867              * Fires when the iframe editor is updated with content from the textarea.
25868              * @param {HtmlEditor} this
25869              * @param {String} html
25870              */
25871             push: true,
25872              /**
25873              * @event editmodechange
25874              * Fires when the editor switches edit modes
25875              * @param {HtmlEditor} this
25876              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25877              */
25878             editmodechange: true,
25879             /**
25880              * @event editorevent
25881              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25882              * @param {HtmlEditor} this
25883              */
25884             editorevent: true,
25885             /**
25886              * @event firstfocus
25887              * Fires when on first focus - needed by toolbars..
25888              * @param {HtmlEditor} this
25889              */
25890             firstfocus: true,
25891             /**
25892              * @event autosave
25893              * Auto save the htmlEditor value as a file into Events
25894              * @param {HtmlEditor} this
25895              */
25896             autosave: true,
25897             /**
25898              * @event savedpreview
25899              * preview the saved version of htmlEditor
25900              * @param {HtmlEditor} this
25901              */
25902             savedpreview: true
25903         });
25904 };
25905
25906
25907 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25908     
25909     
25910       /**
25911      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25912      */
25913     toolbars : false,
25914     
25915      /**
25916     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25917     */
25918     btns : [],
25919    
25920      /**
25921      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25922      *                        Roo.resizable.
25923      */
25924     resizable : false,
25925      /**
25926      * @cfg {Number} height (in pixels)
25927      */   
25928     height: 300,
25929    /**
25930      * @cfg {Number} width (in pixels)
25931      */   
25932     width: false,
25933     
25934     /**
25935      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25936      * 
25937      */
25938     stylesheets: false,
25939     
25940     // id of frame..
25941     frameId: false,
25942     
25943     // private properties
25944     validationEvent : false,
25945     deferHeight: true,
25946     initialized : false,
25947     activated : false,
25948     
25949     onFocus : Roo.emptyFn,
25950     iframePad:3,
25951     hideMode:'offsets',
25952     
25953     tbContainer : false,
25954     
25955     bodyCls : '',
25956     
25957     toolbarContainer :function() {
25958         return this.wrap.select('.x-html-editor-tb',true).first();
25959     },
25960
25961     /**
25962      * Protected method that will not generally be called directly. It
25963      * is called when the editor creates its toolbar. Override this method if you need to
25964      * add custom toolbar buttons.
25965      * @param {HtmlEditor} editor
25966      */
25967     createToolbar : function(){
25968         Roo.log('renewing');
25969         Roo.log("create toolbars");
25970         
25971         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25972         this.toolbars[0].render(this.toolbarContainer());
25973         
25974         return;
25975         
25976 //        if (!editor.toolbars || !editor.toolbars.length) {
25977 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25978 //        }
25979 //        
25980 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25981 //            editor.toolbars[i] = Roo.factory(
25982 //                    typeof(editor.toolbars[i]) == 'string' ?
25983 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25984 //                Roo.bootstrap.HtmlEditor);
25985 //            editor.toolbars[i].init(editor);
25986 //        }
25987     },
25988
25989      
25990     // private
25991     onRender : function(ct, position)
25992     {
25993        // Roo.log("Call onRender: " + this.xtype);
25994         var _t = this;
25995         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25996       
25997         this.wrap = this.inputEl().wrap({
25998             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25999         });
26000         
26001         this.editorcore.onRender(ct, position);
26002          
26003         if (this.resizable) {
26004             this.resizeEl = new Roo.Resizable(this.wrap, {
26005                 pinned : true,
26006                 wrap: true,
26007                 dynamic : true,
26008                 minHeight : this.height,
26009                 height: this.height,
26010                 handles : this.resizable,
26011                 width: this.width,
26012                 listeners : {
26013                     resize : function(r, w, h) {
26014                         _t.onResize(w,h); // -something
26015                     }
26016                 }
26017             });
26018             
26019         }
26020         this.createToolbar(this);
26021        
26022         
26023         if(!this.width && this.resizable){
26024             this.setSize(this.wrap.getSize());
26025         }
26026         if (this.resizeEl) {
26027             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26028             // should trigger onReize..
26029         }
26030         
26031     },
26032
26033     // private
26034     onResize : function(w, h)
26035     {
26036         Roo.log('resize: ' +w + ',' + h );
26037         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26038         var ew = false;
26039         var eh = false;
26040         
26041         if(this.inputEl() ){
26042             if(typeof w == 'number'){
26043                 var aw = w - this.wrap.getFrameWidth('lr');
26044                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26045                 ew = aw;
26046             }
26047             if(typeof h == 'number'){
26048                  var tbh = -11;  // fixme it needs to tool bar size!
26049                 for (var i =0; i < this.toolbars.length;i++) {
26050                     // fixme - ask toolbars for heights?
26051                     tbh += this.toolbars[i].el.getHeight();
26052                     //if (this.toolbars[i].footer) {
26053                     //    tbh += this.toolbars[i].footer.el.getHeight();
26054                     //}
26055                 }
26056               
26057                 
26058                 
26059                 
26060                 
26061                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26062                 ah -= 5; // knock a few pixes off for look..
26063                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26064                 var eh = ah;
26065             }
26066         }
26067         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26068         this.editorcore.onResize(ew,eh);
26069         
26070     },
26071
26072     /**
26073      * Toggles the editor between standard and source edit mode.
26074      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26075      */
26076     toggleSourceEdit : function(sourceEditMode)
26077     {
26078         this.editorcore.toggleSourceEdit(sourceEditMode);
26079         
26080         if(this.editorcore.sourceEditMode){
26081             Roo.log('editor - showing textarea');
26082             
26083 //            Roo.log('in');
26084 //            Roo.log(this.syncValue());
26085             this.syncValue();
26086             this.inputEl().removeClass(['hide', 'x-hidden']);
26087             this.inputEl().dom.removeAttribute('tabIndex');
26088             this.inputEl().focus();
26089         }else{
26090             Roo.log('editor - hiding textarea');
26091 //            Roo.log('out')
26092 //            Roo.log(this.pushValue()); 
26093             this.pushValue();
26094             
26095             this.inputEl().addClass(['hide', 'x-hidden']);
26096             this.inputEl().dom.setAttribute('tabIndex', -1);
26097             //this.deferFocus();
26098         }
26099          
26100         if(this.resizable){
26101             this.setSize(this.wrap.getSize());
26102         }
26103         
26104         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26105     },
26106  
26107     // private (for BoxComponent)
26108     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26109
26110     // private (for BoxComponent)
26111     getResizeEl : function(){
26112         return this.wrap;
26113     },
26114
26115     // private (for BoxComponent)
26116     getPositionEl : function(){
26117         return this.wrap;
26118     },
26119
26120     // private
26121     initEvents : function(){
26122         this.originalValue = this.getValue();
26123     },
26124
26125 //    /**
26126 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26127 //     * @method
26128 //     */
26129 //    markInvalid : Roo.emptyFn,
26130 //    /**
26131 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26132 //     * @method
26133 //     */
26134 //    clearInvalid : Roo.emptyFn,
26135
26136     setValue : function(v){
26137         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26138         this.editorcore.pushValue();
26139     },
26140
26141      
26142     // private
26143     deferFocus : function(){
26144         this.focus.defer(10, this);
26145     },
26146
26147     // doc'ed in Field
26148     focus : function(){
26149         this.editorcore.focus();
26150         
26151     },
26152       
26153
26154     // private
26155     onDestroy : function(){
26156         
26157         
26158         
26159         if(this.rendered){
26160             
26161             for (var i =0; i < this.toolbars.length;i++) {
26162                 // fixme - ask toolbars for heights?
26163                 this.toolbars[i].onDestroy();
26164             }
26165             
26166             this.wrap.dom.innerHTML = '';
26167             this.wrap.remove();
26168         }
26169     },
26170
26171     // private
26172     onFirstFocus : function(){
26173         //Roo.log("onFirstFocus");
26174         this.editorcore.onFirstFocus();
26175          for (var i =0; i < this.toolbars.length;i++) {
26176             this.toolbars[i].onFirstFocus();
26177         }
26178         
26179     },
26180     
26181     // private
26182     syncValue : function()
26183     {   
26184         this.editorcore.syncValue();
26185     },
26186     
26187     pushValue : function()
26188     {   
26189         this.editorcore.pushValue();
26190     }
26191      
26192     
26193     // hide stuff that is not compatible
26194     /**
26195      * @event blur
26196      * @hide
26197      */
26198     /**
26199      * @event change
26200      * @hide
26201      */
26202     /**
26203      * @event focus
26204      * @hide
26205      */
26206     /**
26207      * @event specialkey
26208      * @hide
26209      */
26210     /**
26211      * @cfg {String} fieldClass @hide
26212      */
26213     /**
26214      * @cfg {String} focusClass @hide
26215      */
26216     /**
26217      * @cfg {String} autoCreate @hide
26218      */
26219     /**
26220      * @cfg {String} inputType @hide
26221      */
26222      
26223     /**
26224      * @cfg {String} invalidText @hide
26225      */
26226     /**
26227      * @cfg {String} msgFx @hide
26228      */
26229     /**
26230      * @cfg {String} validateOnBlur @hide
26231      */
26232 });
26233  
26234     
26235    
26236    
26237    
26238       
26239 Roo.namespace('Roo.bootstrap.htmleditor');
26240 /**
26241  * @class Roo.bootstrap.HtmlEditorToolbar1
26242  * Basic Toolbar
26243  * 
26244  * @example
26245  * Usage:
26246  *
26247  new Roo.bootstrap.HtmlEditor({
26248     ....
26249     toolbars : [
26250         new Roo.bootstrap.HtmlEditorToolbar1({
26251             disable : { fonts: 1 , format: 1, ..., ... , ...],
26252             btns : [ .... ]
26253         })
26254     }
26255      
26256  * 
26257  * @cfg {Object} disable List of elements to disable..
26258  * @cfg {Array} btns List of additional buttons.
26259  * 
26260  * 
26261  * NEEDS Extra CSS? 
26262  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26263  */
26264  
26265 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26266 {
26267     
26268     Roo.apply(this, config);
26269     
26270     // default disabled, based on 'good practice'..
26271     this.disable = this.disable || {};
26272     Roo.applyIf(this.disable, {
26273         fontSize : true,
26274         colors : true,
26275         specialElements : true
26276     });
26277     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26278     
26279     this.editor = config.editor;
26280     this.editorcore = config.editor.editorcore;
26281     
26282     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26283     
26284     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26285     // dont call parent... till later.
26286 }
26287 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26288      
26289     bar : true,
26290     
26291     editor : false,
26292     editorcore : false,
26293     
26294     
26295     formats : [
26296         "p" ,  
26297         "h1","h2","h3","h4","h5","h6", 
26298         "pre", "code", 
26299         "abbr", "acronym", "address", "cite", "samp", "var",
26300         'div','span'
26301     ],
26302     
26303     onRender : function(ct, position)
26304     {
26305        // Roo.log("Call onRender: " + this.xtype);
26306         
26307        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26308        Roo.log(this.el);
26309        this.el.dom.style.marginBottom = '0';
26310        var _this = this;
26311        var editorcore = this.editorcore;
26312        var editor= this.editor;
26313        
26314        var children = [];
26315        var btn = function(id,cmd , toggle, handler, html){
26316        
26317             var  event = toggle ? 'toggle' : 'click';
26318        
26319             var a = {
26320                 size : 'sm',
26321                 xtype: 'Button',
26322                 xns: Roo.bootstrap,
26323                 //glyphicon : id,
26324                 fa: id,
26325                 cmd : id || cmd,
26326                 enableToggle:toggle !== false,
26327                 html : html || '',
26328                 pressed : toggle ? false : null,
26329                 listeners : {}
26330             };
26331             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26332                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26333             };
26334             children.push(a);
26335             return a;
26336        }
26337        
26338     //    var cb_box = function...
26339         
26340         var style = {
26341                 xtype: 'Button',
26342                 size : 'sm',
26343                 xns: Roo.bootstrap,
26344                 fa : 'font',
26345                 //html : 'submit'
26346                 menu : {
26347                     xtype: 'Menu',
26348                     xns: Roo.bootstrap,
26349                     items:  []
26350                 }
26351         };
26352         Roo.each(this.formats, function(f) {
26353             style.menu.items.push({
26354                 xtype :'MenuItem',
26355                 xns: Roo.bootstrap,
26356                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26357                 tagname : f,
26358                 listeners : {
26359                     click : function()
26360                     {
26361                         editorcore.insertTag(this.tagname);
26362                         editor.focus();
26363                     }
26364                 }
26365                 
26366             });
26367         });
26368         children.push(style);   
26369         
26370         btn('bold',false,true);
26371         btn('italic',false,true);
26372         btn('align-left', 'justifyleft',true);
26373         btn('align-center', 'justifycenter',true);
26374         btn('align-right' , 'justifyright',true);
26375         btn('link', false, false, function(btn) {
26376             //Roo.log("create link?");
26377             var url = prompt(this.createLinkText, this.defaultLinkValue);
26378             if(url && url != 'http:/'+'/'){
26379                 this.editorcore.relayCmd('createlink', url);
26380             }
26381         }),
26382         btn('list','insertunorderedlist',true);
26383         btn('pencil', false,true, function(btn){
26384                 Roo.log(this);
26385                 this.toggleSourceEdit(btn.pressed);
26386         });
26387         
26388         if (this.editor.btns.length > 0) {
26389             for (var i = 0; i<this.editor.btns.length; i++) {
26390                 children.push(this.editor.btns[i]);
26391             }
26392         }
26393         
26394         /*
26395         var cog = {
26396                 xtype: 'Button',
26397                 size : 'sm',
26398                 xns: Roo.bootstrap,
26399                 glyphicon : 'cog',
26400                 //html : 'submit'
26401                 menu : {
26402                     xtype: 'Menu',
26403                     xns: Roo.bootstrap,
26404                     items:  []
26405                 }
26406         };
26407         
26408         cog.menu.items.push({
26409             xtype :'MenuItem',
26410             xns: Roo.bootstrap,
26411             html : Clean styles,
26412             tagname : f,
26413             listeners : {
26414                 click : function()
26415                 {
26416                     editorcore.insertTag(this.tagname);
26417                     editor.focus();
26418                 }
26419             }
26420             
26421         });
26422        */
26423         
26424          
26425        this.xtype = 'NavSimplebar';
26426         
26427         for(var i=0;i< children.length;i++) {
26428             
26429             this.buttons.add(this.addxtypeChild(children[i]));
26430             
26431         }
26432         
26433         editor.on('editorevent', this.updateToolbar, this);
26434     },
26435     onBtnClick : function(id)
26436     {
26437        this.editorcore.relayCmd(id);
26438        this.editorcore.focus();
26439     },
26440     
26441     /**
26442      * Protected method that will not generally be called directly. It triggers
26443      * a toolbar update by reading the markup state of the current selection in the editor.
26444      */
26445     updateToolbar: function(){
26446
26447         if(!this.editorcore.activated){
26448             this.editor.onFirstFocus(); // is this neeed?
26449             return;
26450         }
26451
26452         var btns = this.buttons; 
26453         var doc = this.editorcore.doc;
26454         btns.get('bold').setActive(doc.queryCommandState('bold'));
26455         btns.get('italic').setActive(doc.queryCommandState('italic'));
26456         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26457         
26458         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26459         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26460         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26461         
26462         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26463         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26464          /*
26465         
26466         var ans = this.editorcore.getAllAncestors();
26467         if (this.formatCombo) {
26468             
26469             
26470             var store = this.formatCombo.store;
26471             this.formatCombo.setValue("");
26472             for (var i =0; i < ans.length;i++) {
26473                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26474                     // select it..
26475                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26476                     break;
26477                 }
26478             }
26479         }
26480         
26481         
26482         
26483         // hides menus... - so this cant be on a menu...
26484         Roo.bootstrap.MenuMgr.hideAll();
26485         */
26486         Roo.bootstrap.MenuMgr.hideAll();
26487         //this.editorsyncValue();
26488     },
26489     onFirstFocus: function() {
26490         this.buttons.each(function(item){
26491            item.enable();
26492         });
26493     },
26494     toggleSourceEdit : function(sourceEditMode){
26495         
26496           
26497         if(sourceEditMode){
26498             Roo.log("disabling buttons");
26499            this.buttons.each( function(item){
26500                 if(item.cmd != 'pencil'){
26501                     item.disable();
26502                 }
26503             });
26504           
26505         }else{
26506             Roo.log("enabling buttons");
26507             if(this.editorcore.initialized){
26508                 this.buttons.each( function(item){
26509                     item.enable();
26510                 });
26511             }
26512             
26513         }
26514         Roo.log("calling toggole on editor");
26515         // tell the editor that it's been pressed..
26516         this.editor.toggleSourceEdit(sourceEditMode);
26517        
26518     }
26519 });
26520
26521
26522
26523
26524  
26525 /*
26526  * - LGPL
26527  */
26528
26529 /**
26530  * @class Roo.bootstrap.Markdown
26531  * @extends Roo.bootstrap.TextArea
26532  * Bootstrap Showdown editable area
26533  * @cfg {string} content
26534  * 
26535  * @constructor
26536  * Create a new Showdown
26537  */
26538
26539 Roo.bootstrap.Markdown = function(config){
26540     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26541    
26542 };
26543
26544 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26545     
26546     editing :false,
26547     
26548     initEvents : function()
26549     {
26550         
26551         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26552         this.markdownEl = this.el.createChild({
26553             cls : 'roo-markdown-area'
26554         });
26555         this.inputEl().addClass('d-none');
26556         if (this.getValue() == '') {
26557             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26558             
26559         } else {
26560             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26561         }
26562         this.markdownEl.on('click', this.toggleTextEdit, this);
26563         this.on('blur', this.toggleTextEdit, this);
26564         this.on('specialkey', this.resizeTextArea, this);
26565     },
26566     
26567     toggleTextEdit : function()
26568     {
26569         var sh = this.markdownEl.getHeight();
26570         this.inputEl().addClass('d-none');
26571         this.markdownEl.addClass('d-none');
26572         if (!this.editing) {
26573             // show editor?
26574             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26575             this.inputEl().removeClass('d-none');
26576             this.inputEl().focus();
26577             this.editing = true;
26578             return;
26579         }
26580         // show showdown...
26581         this.updateMarkdown();
26582         this.markdownEl.removeClass('d-none');
26583         this.editing = false;
26584         return;
26585     },
26586     updateMarkdown : function()
26587     {
26588         if (this.getValue() == '') {
26589             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26590             return;
26591         }
26592  
26593         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26594     },
26595     
26596     resizeTextArea: function () {
26597         
26598         var sh = 100;
26599         Roo.log([sh, this.getValue().split("\n").length * 30]);
26600         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26601     },
26602     setValue : function(val)
26603     {
26604         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26605         if (!this.editing) {
26606             this.updateMarkdown();
26607         }
26608         
26609     },
26610     focus : function()
26611     {
26612         if (!this.editing) {
26613             this.toggleTextEdit();
26614         }
26615         
26616     }
26617
26618
26619 });
26620 /**
26621  * @class Roo.bootstrap.Table.AbstractSelectionModel
26622  * @extends Roo.util.Observable
26623  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26624  * implemented by descendant classes.  This class should not be directly instantiated.
26625  * @constructor
26626  */
26627 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26628     this.locked = false;
26629     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26630 };
26631
26632
26633 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26634     /** @ignore Called by the grid automatically. Do not call directly. */
26635     init : function(grid){
26636         this.grid = grid;
26637         this.initEvents();
26638     },
26639
26640     /**
26641      * Locks the selections.
26642      */
26643     lock : function(){
26644         this.locked = true;
26645     },
26646
26647     /**
26648      * Unlocks the selections.
26649      */
26650     unlock : function(){
26651         this.locked = false;
26652     },
26653
26654     /**
26655      * Returns true if the selections are locked.
26656      * @return {Boolean}
26657      */
26658     isLocked : function(){
26659         return this.locked;
26660     },
26661     
26662     
26663     initEvents : function ()
26664     {
26665         
26666     }
26667 });
26668 /**
26669  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26670  * @class Roo.bootstrap.Table.RowSelectionModel
26671  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26672  * It supports multiple selections and keyboard selection/navigation. 
26673  * @constructor
26674  * @param {Object} config
26675  */
26676
26677 Roo.bootstrap.Table.RowSelectionModel = function(config){
26678     Roo.apply(this, config);
26679     this.selections = new Roo.util.MixedCollection(false, function(o){
26680         return o.id;
26681     });
26682
26683     this.last = false;
26684     this.lastActive = false;
26685
26686     this.addEvents({
26687         /**
26688              * @event selectionchange
26689              * Fires when the selection changes
26690              * @param {SelectionModel} this
26691              */
26692             "selectionchange" : true,
26693         /**
26694              * @event afterselectionchange
26695              * Fires after the selection changes (eg. by key press or clicking)
26696              * @param {SelectionModel} this
26697              */
26698             "afterselectionchange" : true,
26699         /**
26700              * @event beforerowselect
26701              * Fires when a row is selected being selected, return false to cancel.
26702              * @param {SelectionModel} this
26703              * @param {Number} rowIndex The selected index
26704              * @param {Boolean} keepExisting False if other selections will be cleared
26705              */
26706             "beforerowselect" : true,
26707         /**
26708              * @event rowselect
26709              * Fires when a row is selected.
26710              * @param {SelectionModel} this
26711              * @param {Number} rowIndex The selected index
26712              * @param {Roo.data.Record} r The record
26713              */
26714             "rowselect" : true,
26715         /**
26716              * @event rowdeselect
26717              * Fires when a row is deselected.
26718              * @param {SelectionModel} this
26719              * @param {Number} rowIndex The selected index
26720              */
26721         "rowdeselect" : true
26722     });
26723     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26724     this.locked = false;
26725  };
26726
26727 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26728     /**
26729      * @cfg {Boolean} singleSelect
26730      * True to allow selection of only one row at a time (defaults to false)
26731      */
26732     singleSelect : false,
26733
26734     // private
26735     initEvents : function()
26736     {
26737
26738         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26739         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26740         //}else{ // allow click to work like normal
26741          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26742         //}
26743         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26744         this.grid.on("rowclick", this.handleMouseDown, this);
26745         
26746         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26747             "up" : function(e){
26748                 if(!e.shiftKey){
26749                     this.selectPrevious(e.shiftKey);
26750                 }else if(this.last !== false && this.lastActive !== false){
26751                     var last = this.last;
26752                     this.selectRange(this.last,  this.lastActive-1);
26753                     this.grid.getView().focusRow(this.lastActive);
26754                     if(last !== false){
26755                         this.last = last;
26756                     }
26757                 }else{
26758                     this.selectFirstRow();
26759                 }
26760                 this.fireEvent("afterselectionchange", this);
26761             },
26762             "down" : function(e){
26763                 if(!e.shiftKey){
26764                     this.selectNext(e.shiftKey);
26765                 }else if(this.last !== false && this.lastActive !== false){
26766                     var last = this.last;
26767                     this.selectRange(this.last,  this.lastActive+1);
26768                     this.grid.getView().focusRow(this.lastActive);
26769                     if(last !== false){
26770                         this.last = last;
26771                     }
26772                 }else{
26773                     this.selectFirstRow();
26774                 }
26775                 this.fireEvent("afterselectionchange", this);
26776             },
26777             scope: this
26778         });
26779         this.grid.store.on('load', function(){
26780             this.selections.clear();
26781         },this);
26782         /*
26783         var view = this.grid.view;
26784         view.on("refresh", this.onRefresh, this);
26785         view.on("rowupdated", this.onRowUpdated, this);
26786         view.on("rowremoved", this.onRemove, this);
26787         */
26788     },
26789
26790     // private
26791     onRefresh : function()
26792     {
26793         var ds = this.grid.store, i, v = this.grid.view;
26794         var s = this.selections;
26795         s.each(function(r){
26796             if((i = ds.indexOfId(r.id)) != -1){
26797                 v.onRowSelect(i);
26798             }else{
26799                 s.remove(r);
26800             }
26801         });
26802     },
26803
26804     // private
26805     onRemove : function(v, index, r){
26806         this.selections.remove(r);
26807     },
26808
26809     // private
26810     onRowUpdated : function(v, index, r){
26811         if(this.isSelected(r)){
26812             v.onRowSelect(index);
26813         }
26814     },
26815
26816     /**
26817      * Select records.
26818      * @param {Array} records The records to select
26819      * @param {Boolean} keepExisting (optional) True to keep existing selections
26820      */
26821     selectRecords : function(records, keepExisting)
26822     {
26823         if(!keepExisting){
26824             this.clearSelections();
26825         }
26826             var ds = this.grid.store;
26827         for(var i = 0, len = records.length; i < len; i++){
26828             this.selectRow(ds.indexOf(records[i]), true);
26829         }
26830     },
26831
26832     /**
26833      * Gets the number of selected rows.
26834      * @return {Number}
26835      */
26836     getCount : function(){
26837         return this.selections.length;
26838     },
26839
26840     /**
26841      * Selects the first row in the grid.
26842      */
26843     selectFirstRow : function(){
26844         this.selectRow(0);
26845     },
26846
26847     /**
26848      * Select the last row.
26849      * @param {Boolean} keepExisting (optional) True to keep existing selections
26850      */
26851     selectLastRow : function(keepExisting){
26852         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26853         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26854     },
26855
26856     /**
26857      * Selects the row immediately following the last selected row.
26858      * @param {Boolean} keepExisting (optional) True to keep existing selections
26859      */
26860     selectNext : function(keepExisting)
26861     {
26862             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26863             this.selectRow(this.last+1, keepExisting);
26864             this.grid.getView().focusRow(this.last);
26865         }
26866     },
26867
26868     /**
26869      * Selects the row that precedes the last selected row.
26870      * @param {Boolean} keepExisting (optional) True to keep existing selections
26871      */
26872     selectPrevious : function(keepExisting){
26873         if(this.last){
26874             this.selectRow(this.last-1, keepExisting);
26875             this.grid.getView().focusRow(this.last);
26876         }
26877     },
26878
26879     /**
26880      * Returns the selected records
26881      * @return {Array} Array of selected records
26882      */
26883     getSelections : function(){
26884         return [].concat(this.selections.items);
26885     },
26886
26887     /**
26888      * Returns the first selected record.
26889      * @return {Record}
26890      */
26891     getSelected : function(){
26892         return this.selections.itemAt(0);
26893     },
26894
26895
26896     /**
26897      * Clears all selections.
26898      */
26899     clearSelections : function(fast)
26900     {
26901         if(this.locked) {
26902             return;
26903         }
26904         if(fast !== true){
26905                 var ds = this.grid.store;
26906             var s = this.selections;
26907             s.each(function(r){
26908                 this.deselectRow(ds.indexOfId(r.id));
26909             }, this);
26910             s.clear();
26911         }else{
26912             this.selections.clear();
26913         }
26914         this.last = false;
26915     },
26916
26917
26918     /**
26919      * Selects all rows.
26920      */
26921     selectAll : function(){
26922         if(this.locked) {
26923             return;
26924         }
26925         this.selections.clear();
26926         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26927             this.selectRow(i, true);
26928         }
26929     },
26930
26931     /**
26932      * Returns True if there is a selection.
26933      * @return {Boolean}
26934      */
26935     hasSelection : function(){
26936         return this.selections.length > 0;
26937     },
26938
26939     /**
26940      * Returns True if the specified row is selected.
26941      * @param {Number/Record} record The record or index of the record to check
26942      * @return {Boolean}
26943      */
26944     isSelected : function(index){
26945             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26946         return (r && this.selections.key(r.id) ? true : false);
26947     },
26948
26949     /**
26950      * Returns True if the specified record id is selected.
26951      * @param {String} id The id of record to check
26952      * @return {Boolean}
26953      */
26954     isIdSelected : function(id){
26955         return (this.selections.key(id) ? true : false);
26956     },
26957
26958
26959     // private
26960     handleMouseDBClick : function(e, t){
26961         
26962     },
26963     // private
26964     handleMouseDown : function(e, t)
26965     {
26966             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26967         if(this.isLocked() || rowIndex < 0 ){
26968             return;
26969         };
26970         if(e.shiftKey && this.last !== false){
26971             var last = this.last;
26972             this.selectRange(last, rowIndex, e.ctrlKey);
26973             this.last = last; // reset the last
26974             t.focus();
26975     
26976         }else{
26977             var isSelected = this.isSelected(rowIndex);
26978             //Roo.log("select row:" + rowIndex);
26979             if(isSelected){
26980                 this.deselectRow(rowIndex);
26981             } else {
26982                         this.selectRow(rowIndex, true);
26983             }
26984     
26985             /*
26986                 if(e.button !== 0 && isSelected){
26987                 alert('rowIndex 2: ' + rowIndex);
26988                     view.focusRow(rowIndex);
26989                 }else if(e.ctrlKey && isSelected){
26990                     this.deselectRow(rowIndex);
26991                 }else if(!isSelected){
26992                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26993                     view.focusRow(rowIndex);
26994                 }
26995             */
26996         }
26997         this.fireEvent("afterselectionchange", this);
26998     },
26999     // private
27000     handleDragableRowClick :  function(grid, rowIndex, e) 
27001     {
27002         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27003             this.selectRow(rowIndex, false);
27004             grid.view.focusRow(rowIndex);
27005              this.fireEvent("afterselectionchange", this);
27006         }
27007     },
27008     
27009     /**
27010      * Selects multiple rows.
27011      * @param {Array} rows Array of the indexes of the row to select
27012      * @param {Boolean} keepExisting (optional) True to keep existing selections
27013      */
27014     selectRows : function(rows, keepExisting){
27015         if(!keepExisting){
27016             this.clearSelections();
27017         }
27018         for(var i = 0, len = rows.length; i < len; i++){
27019             this.selectRow(rows[i], true);
27020         }
27021     },
27022
27023     /**
27024      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27025      * @param {Number} startRow The index of the first row in the range
27026      * @param {Number} endRow The index of the last row in the range
27027      * @param {Boolean} keepExisting (optional) True to retain existing selections
27028      */
27029     selectRange : function(startRow, endRow, keepExisting){
27030         if(this.locked) {
27031             return;
27032         }
27033         if(!keepExisting){
27034             this.clearSelections();
27035         }
27036         if(startRow <= endRow){
27037             for(var i = startRow; i <= endRow; i++){
27038                 this.selectRow(i, true);
27039             }
27040         }else{
27041             for(var i = startRow; i >= endRow; i--){
27042                 this.selectRow(i, true);
27043             }
27044         }
27045     },
27046
27047     /**
27048      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27049      * @param {Number} startRow The index of the first row in the range
27050      * @param {Number} endRow The index of the last row in the range
27051      */
27052     deselectRange : function(startRow, endRow, preventViewNotify){
27053         if(this.locked) {
27054             return;
27055         }
27056         for(var i = startRow; i <= endRow; i++){
27057             this.deselectRow(i, preventViewNotify);
27058         }
27059     },
27060
27061     /**
27062      * Selects a row.
27063      * @param {Number} row The index of the row to select
27064      * @param {Boolean} keepExisting (optional) True to keep existing selections
27065      */
27066     selectRow : function(index, keepExisting, preventViewNotify)
27067     {
27068             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27069             return;
27070         }
27071         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27072             if(!keepExisting || this.singleSelect){
27073                 this.clearSelections();
27074             }
27075             
27076             var r = this.grid.store.getAt(index);
27077             //console.log('selectRow - record id :' + r.id);
27078             
27079             this.selections.add(r);
27080             this.last = this.lastActive = index;
27081             if(!preventViewNotify){
27082                 var proxy = new Roo.Element(
27083                                 this.grid.getRowDom(index)
27084                 );
27085                 proxy.addClass('bg-info info');
27086             }
27087             this.fireEvent("rowselect", this, index, r);
27088             this.fireEvent("selectionchange", this);
27089         }
27090     },
27091
27092     /**
27093      * Deselects a row.
27094      * @param {Number} row The index of the row to deselect
27095      */
27096     deselectRow : function(index, preventViewNotify)
27097     {
27098         if(this.locked) {
27099             return;
27100         }
27101         if(this.last == index){
27102             this.last = false;
27103         }
27104         if(this.lastActive == index){
27105             this.lastActive = false;
27106         }
27107         
27108         var r = this.grid.store.getAt(index);
27109         if (!r) {
27110             return;
27111         }
27112         
27113         this.selections.remove(r);
27114         //.console.log('deselectRow - record id :' + r.id);
27115         if(!preventViewNotify){
27116         
27117             var proxy = new Roo.Element(
27118                 this.grid.getRowDom(index)
27119             );
27120             proxy.removeClass('bg-info info');
27121         }
27122         this.fireEvent("rowdeselect", this, index);
27123         this.fireEvent("selectionchange", this);
27124     },
27125
27126     // private
27127     restoreLast : function(){
27128         if(this._last){
27129             this.last = this._last;
27130         }
27131     },
27132
27133     // private
27134     acceptsNav : function(row, col, cm){
27135         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27136     },
27137
27138     // private
27139     onEditorKey : function(field, e){
27140         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27141         if(k == e.TAB){
27142             e.stopEvent();
27143             ed.completeEdit();
27144             if(e.shiftKey){
27145                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27146             }else{
27147                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27148             }
27149         }else if(k == e.ENTER && !e.ctrlKey){
27150             e.stopEvent();
27151             ed.completeEdit();
27152             if(e.shiftKey){
27153                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27154             }else{
27155                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27156             }
27157         }else if(k == e.ESC){
27158             ed.cancelEdit();
27159         }
27160         if(newCell){
27161             g.startEditing(newCell[0], newCell[1]);
27162         }
27163     }
27164 });
27165 /*
27166  * Based on:
27167  * Ext JS Library 1.1.1
27168  * Copyright(c) 2006-2007, Ext JS, LLC.
27169  *
27170  * Originally Released Under LGPL - original licence link has changed is not relivant.
27171  *
27172  * Fork - LGPL
27173  * <script type="text/javascript">
27174  */
27175  
27176 /**
27177  * @class Roo.bootstrap.PagingToolbar
27178  * @extends Roo.bootstrap.NavSimplebar
27179  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27180  * @constructor
27181  * Create a new PagingToolbar
27182  * @param {Object} config The config object
27183  * @param {Roo.data.Store} store
27184  */
27185 Roo.bootstrap.PagingToolbar = function(config)
27186 {
27187     // old args format still supported... - xtype is prefered..
27188         // created from xtype...
27189     
27190     this.ds = config.dataSource;
27191     
27192     if (config.store && !this.ds) {
27193         this.store= Roo.factory(config.store, Roo.data);
27194         this.ds = this.store;
27195         this.ds.xmodule = this.xmodule || false;
27196     }
27197     
27198     this.toolbarItems = [];
27199     if (config.items) {
27200         this.toolbarItems = config.items;
27201     }
27202     
27203     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27204     
27205     this.cursor = 0;
27206     
27207     if (this.ds) { 
27208         this.bind(this.ds);
27209     }
27210     
27211     if (Roo.bootstrap.version == 4) {
27212         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27213     } else {
27214         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27215     }
27216     
27217 };
27218
27219 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27220     /**
27221      * @cfg {Roo.data.Store} dataSource
27222      * The underlying data store providing the paged data
27223      */
27224     /**
27225      * @cfg {String/HTMLElement/Element} container
27226      * container The id or element that will contain the toolbar
27227      */
27228     /**
27229      * @cfg {Boolean} displayInfo
27230      * True to display the displayMsg (defaults to false)
27231      */
27232     /**
27233      * @cfg {Number} pageSize
27234      * The number of records to display per page (defaults to 20)
27235      */
27236     pageSize: 20,
27237     /**
27238      * @cfg {String} displayMsg
27239      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27240      */
27241     displayMsg : 'Displaying {0} - {1} of {2}',
27242     /**
27243      * @cfg {String} emptyMsg
27244      * The message to display when no records are found (defaults to "No data to display")
27245      */
27246     emptyMsg : 'No data to display',
27247     /**
27248      * Customizable piece of the default paging text (defaults to "Page")
27249      * @type String
27250      */
27251     beforePageText : "Page",
27252     /**
27253      * Customizable piece of the default paging text (defaults to "of %0")
27254      * @type String
27255      */
27256     afterPageText : "of {0}",
27257     /**
27258      * Customizable piece of the default paging text (defaults to "First Page")
27259      * @type String
27260      */
27261     firstText : "First Page",
27262     /**
27263      * Customizable piece of the default paging text (defaults to "Previous Page")
27264      * @type String
27265      */
27266     prevText : "Previous Page",
27267     /**
27268      * Customizable piece of the default paging text (defaults to "Next Page")
27269      * @type String
27270      */
27271     nextText : "Next Page",
27272     /**
27273      * Customizable piece of the default paging text (defaults to "Last Page")
27274      * @type String
27275      */
27276     lastText : "Last Page",
27277     /**
27278      * Customizable piece of the default paging text (defaults to "Refresh")
27279      * @type String
27280      */
27281     refreshText : "Refresh",
27282
27283     buttons : false,
27284     // private
27285     onRender : function(ct, position) 
27286     {
27287         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27288         this.navgroup.parentId = this.id;
27289         this.navgroup.onRender(this.el, null);
27290         // add the buttons to the navgroup
27291         
27292         if(this.displayInfo){
27293             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27294             this.displayEl = this.el.select('.x-paging-info', true).first();
27295 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27296 //            this.displayEl = navel.el.select('span',true).first();
27297         }
27298         
27299         var _this = this;
27300         
27301         if(this.buttons){
27302             Roo.each(_this.buttons, function(e){ // this might need to use render????
27303                Roo.factory(e).render(_this.el);
27304             });
27305         }
27306             
27307         Roo.each(_this.toolbarItems, function(e) {
27308             _this.navgroup.addItem(e);
27309         });
27310         
27311         
27312         this.first = this.navgroup.addItem({
27313             tooltip: this.firstText,
27314             cls: "prev btn-outline-secondary",
27315             html : ' <i class="fa fa-step-backward"></i>',
27316             disabled: true,
27317             preventDefault: true,
27318             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27319         });
27320         
27321         this.prev =  this.navgroup.addItem({
27322             tooltip: this.prevText,
27323             cls: "prev btn-outline-secondary",
27324             html : ' <i class="fa fa-backward"></i>',
27325             disabled: true,
27326             preventDefault: true,
27327             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27328         });
27329     //this.addSeparator();
27330         
27331         
27332         var field = this.navgroup.addItem( {
27333             tagtype : 'span',
27334             cls : 'x-paging-position  btn-outline-secondary',
27335              disabled: true,
27336             html : this.beforePageText  +
27337                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27338                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27339          } ); //?? escaped?
27340         
27341         this.field = field.el.select('input', true).first();
27342         this.field.on("keydown", this.onPagingKeydown, this);
27343         this.field.on("focus", function(){this.dom.select();});
27344     
27345     
27346         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27347         //this.field.setHeight(18);
27348         //this.addSeparator();
27349         this.next = this.navgroup.addItem({
27350             tooltip: this.nextText,
27351             cls: "next btn-outline-secondary",
27352             html : ' <i class="fa fa-forward"></i>',
27353             disabled: true,
27354             preventDefault: true,
27355             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27356         });
27357         this.last = this.navgroup.addItem({
27358             tooltip: this.lastText,
27359             html : ' <i class="fa fa-step-forward"></i>',
27360             cls: "next btn-outline-secondary",
27361             disabled: true,
27362             preventDefault: true,
27363             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27364         });
27365     //this.addSeparator();
27366         this.loading = this.navgroup.addItem({
27367             tooltip: this.refreshText,
27368             cls: "btn-outline-secondary",
27369             html : ' <i class="fa fa-refresh"></i>',
27370             preventDefault: true,
27371             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27372         });
27373         
27374     },
27375
27376     // private
27377     updateInfo : function(){
27378         if(this.displayEl){
27379             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27380             var msg = count == 0 ?
27381                 this.emptyMsg :
27382                 String.format(
27383                     this.displayMsg,
27384                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27385                 );
27386             this.displayEl.update(msg);
27387         }
27388     },
27389
27390     // private
27391     onLoad : function(ds, r, o)
27392     {
27393         this.cursor = o.params && o.params.start ? o.params.start : 0;
27394         
27395         var d = this.getPageData(),
27396             ap = d.activePage,
27397             ps = d.pages;
27398         
27399         
27400         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27401         this.field.dom.value = ap;
27402         this.first.setDisabled(ap == 1);
27403         this.prev.setDisabled(ap == 1);
27404         this.next.setDisabled(ap == ps);
27405         this.last.setDisabled(ap == ps);
27406         this.loading.enable();
27407         this.updateInfo();
27408     },
27409
27410     // private
27411     getPageData : function(){
27412         var total = this.ds.getTotalCount();
27413         return {
27414             total : total,
27415             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27416             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27417         };
27418     },
27419
27420     // private
27421     onLoadError : function(){
27422         this.loading.enable();
27423     },
27424
27425     // private
27426     onPagingKeydown : function(e){
27427         var k = e.getKey();
27428         var d = this.getPageData();
27429         if(k == e.RETURN){
27430             var v = this.field.dom.value, pageNum;
27431             if(!v || isNaN(pageNum = parseInt(v, 10))){
27432                 this.field.dom.value = d.activePage;
27433                 return;
27434             }
27435             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27436             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27437             e.stopEvent();
27438         }
27439         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))
27440         {
27441           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27442           this.field.dom.value = pageNum;
27443           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27444           e.stopEvent();
27445         }
27446         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27447         {
27448           var v = this.field.dom.value, pageNum; 
27449           var increment = (e.shiftKey) ? 10 : 1;
27450           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27451                 increment *= -1;
27452           }
27453           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27454             this.field.dom.value = d.activePage;
27455             return;
27456           }
27457           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27458           {
27459             this.field.dom.value = parseInt(v, 10) + increment;
27460             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27461             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27462           }
27463           e.stopEvent();
27464         }
27465     },
27466
27467     // private
27468     beforeLoad : function(){
27469         if(this.loading){
27470             this.loading.disable();
27471         }
27472     },
27473
27474     // private
27475     onClick : function(which){
27476         
27477         var ds = this.ds;
27478         if (!ds) {
27479             return;
27480         }
27481         
27482         switch(which){
27483             case "first":
27484                 ds.load({params:{start: 0, limit: this.pageSize}});
27485             break;
27486             case "prev":
27487                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27488             break;
27489             case "next":
27490                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27491             break;
27492             case "last":
27493                 var total = ds.getTotalCount();
27494                 var extra = total % this.pageSize;
27495                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27496                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27497             break;
27498             case "refresh":
27499                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27500             break;
27501         }
27502     },
27503
27504     /**
27505      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27506      * @param {Roo.data.Store} store The data store to unbind
27507      */
27508     unbind : function(ds){
27509         ds.un("beforeload", this.beforeLoad, this);
27510         ds.un("load", this.onLoad, this);
27511         ds.un("loadexception", this.onLoadError, this);
27512         ds.un("remove", this.updateInfo, this);
27513         ds.un("add", this.updateInfo, this);
27514         this.ds = undefined;
27515     },
27516
27517     /**
27518      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27519      * @param {Roo.data.Store} store The data store to bind
27520      */
27521     bind : function(ds){
27522         ds.on("beforeload", this.beforeLoad, this);
27523         ds.on("load", this.onLoad, this);
27524         ds.on("loadexception", this.onLoadError, this);
27525         ds.on("remove", this.updateInfo, this);
27526         ds.on("add", this.updateInfo, this);
27527         this.ds = ds;
27528     }
27529 });/*
27530  * - LGPL
27531  *
27532  * element
27533  * 
27534  */
27535
27536 /**
27537  * @class Roo.bootstrap.MessageBar
27538  * @extends Roo.bootstrap.Component
27539  * Bootstrap MessageBar class
27540  * @cfg {String} html contents of the MessageBar
27541  * @cfg {String} weight (info | success | warning | danger) default info
27542  * @cfg {String} beforeClass insert the bar before the given class
27543  * @cfg {Boolean} closable (true | false) default false
27544  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27545  * 
27546  * @constructor
27547  * Create a new Element
27548  * @param {Object} config The config object
27549  */
27550
27551 Roo.bootstrap.MessageBar = function(config){
27552     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27553 };
27554
27555 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27556     
27557     html: '',
27558     weight: 'info',
27559     closable: false,
27560     fixed: false,
27561     beforeClass: 'bootstrap-sticky-wrap',
27562     
27563     getAutoCreate : function(){
27564         
27565         var cfg = {
27566             tag: 'div',
27567             cls: 'alert alert-dismissable alert-' + this.weight,
27568             cn: [
27569                 {
27570                     tag: 'span',
27571                     cls: 'message',
27572                     html: this.html || ''
27573                 }
27574             ]
27575         };
27576         
27577         if(this.fixed){
27578             cfg.cls += ' alert-messages-fixed';
27579         }
27580         
27581         if(this.closable){
27582             cfg.cn.push({
27583                 tag: 'button',
27584                 cls: 'close',
27585                 html: 'x'
27586             });
27587         }
27588         
27589         return cfg;
27590     },
27591     
27592     onRender : function(ct, position)
27593     {
27594         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27595         
27596         if(!this.el){
27597             var cfg = Roo.apply({},  this.getAutoCreate());
27598             cfg.id = Roo.id();
27599             
27600             if (this.cls) {
27601                 cfg.cls += ' ' + this.cls;
27602             }
27603             if (this.style) {
27604                 cfg.style = this.style;
27605             }
27606             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27607             
27608             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27609         }
27610         
27611         this.el.select('>button.close').on('click', this.hide, this);
27612         
27613     },
27614     
27615     show : function()
27616     {
27617         if (!this.rendered) {
27618             this.render();
27619         }
27620         
27621         this.el.show();
27622         
27623         this.fireEvent('show', this);
27624         
27625     },
27626     
27627     hide : function()
27628     {
27629         if (!this.rendered) {
27630             this.render();
27631         }
27632         
27633         this.el.hide();
27634         
27635         this.fireEvent('hide', this);
27636     },
27637     
27638     update : function()
27639     {
27640 //        var e = this.el.dom.firstChild;
27641 //        
27642 //        if(this.closable){
27643 //            e = e.nextSibling;
27644 //        }
27645 //        
27646 //        e.data = this.html || '';
27647
27648         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27649     }
27650    
27651 });
27652
27653  
27654
27655      /*
27656  * - LGPL
27657  *
27658  * Graph
27659  * 
27660  */
27661
27662
27663 /**
27664  * @class Roo.bootstrap.Graph
27665  * @extends Roo.bootstrap.Component
27666  * Bootstrap Graph class
27667 > Prameters
27668  -sm {number} sm 4
27669  -md {number} md 5
27670  @cfg {String} graphtype  bar | vbar | pie
27671  @cfg {number} g_x coodinator | centre x (pie)
27672  @cfg {number} g_y coodinator | centre y (pie)
27673  @cfg {number} g_r radius (pie)
27674  @cfg {number} g_height height of the chart (respected by all elements in the set)
27675  @cfg {number} g_width width of the chart (respected by all elements in the set)
27676  @cfg {Object} title The title of the chart
27677     
27678  -{Array}  values
27679  -opts (object) options for the chart 
27680      o {
27681      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27682      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27683      o vgutter (number)
27684      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.
27685      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27686      o to
27687      o stretch (boolean)
27688      o }
27689  -opts (object) options for the pie
27690      o{
27691      o cut
27692      o startAngle (number)
27693      o endAngle (number)
27694      } 
27695  *
27696  * @constructor
27697  * Create a new Input
27698  * @param {Object} config The config object
27699  */
27700
27701 Roo.bootstrap.Graph = function(config){
27702     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27703     
27704     this.addEvents({
27705         // img events
27706         /**
27707          * @event click
27708          * The img click event for the img.
27709          * @param {Roo.EventObject} e
27710          */
27711         "click" : true
27712     });
27713 };
27714
27715 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27716     
27717     sm: 4,
27718     md: 5,
27719     graphtype: 'bar',
27720     g_height: 250,
27721     g_width: 400,
27722     g_x: 50,
27723     g_y: 50,
27724     g_r: 30,
27725     opts:{
27726         //g_colors: this.colors,
27727         g_type: 'soft',
27728         g_gutter: '20%'
27729
27730     },
27731     title : false,
27732
27733     getAutoCreate : function(){
27734         
27735         var cfg = {
27736             tag: 'div',
27737             html : null
27738         };
27739         
27740         
27741         return  cfg;
27742     },
27743
27744     onRender : function(ct,position){
27745         
27746         
27747         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27748         
27749         if (typeof(Raphael) == 'undefined') {
27750             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27751             return;
27752         }
27753         
27754         this.raphael = Raphael(this.el.dom);
27755         
27756                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27757                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27758                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27759                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27760                 /*
27761                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27762                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27763                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27764                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27765                 
27766                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27767                 r.barchart(330, 10, 300, 220, data1);
27768                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27769                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27770                 */
27771                 
27772                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27773                 // r.barchart(30, 30, 560, 250,  xdata, {
27774                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27775                 //     axis : "0 0 1 1",
27776                 //     axisxlabels :  xdata
27777                 //     //yvalues : cols,
27778                    
27779                 // });
27780 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27781 //        
27782 //        this.load(null,xdata,{
27783 //                axis : "0 0 1 1",
27784 //                axisxlabels :  xdata
27785 //                });
27786
27787     },
27788
27789     load : function(graphtype,xdata,opts)
27790     {
27791         this.raphael.clear();
27792         if(!graphtype) {
27793             graphtype = this.graphtype;
27794         }
27795         if(!opts){
27796             opts = this.opts;
27797         }
27798         var r = this.raphael,
27799             fin = function () {
27800                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27801             },
27802             fout = function () {
27803                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27804             },
27805             pfin = function() {
27806                 this.sector.stop();
27807                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27808
27809                 if (this.label) {
27810                     this.label[0].stop();
27811                     this.label[0].attr({ r: 7.5 });
27812                     this.label[1].attr({ "font-weight": 800 });
27813                 }
27814             },
27815             pfout = function() {
27816                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27817
27818                 if (this.label) {
27819                     this.label[0].animate({ r: 5 }, 500, "bounce");
27820                     this.label[1].attr({ "font-weight": 400 });
27821                 }
27822             };
27823
27824         switch(graphtype){
27825             case 'bar':
27826                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27827                 break;
27828             case 'hbar':
27829                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27830                 break;
27831             case 'pie':
27832 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27833 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27834 //            
27835                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27836                 
27837                 break;
27838
27839         }
27840         
27841         if(this.title){
27842             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27843         }
27844         
27845     },
27846     
27847     setTitle: function(o)
27848     {
27849         this.title = o;
27850     },
27851     
27852     initEvents: function() {
27853         
27854         if(!this.href){
27855             this.el.on('click', this.onClick, this);
27856         }
27857     },
27858     
27859     onClick : function(e)
27860     {
27861         Roo.log('img onclick');
27862         this.fireEvent('click', this, e);
27863     }
27864    
27865 });
27866
27867  
27868 /*
27869  * - LGPL
27870  *
27871  * numberBox
27872  * 
27873  */
27874 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27875
27876 /**
27877  * @class Roo.bootstrap.dash.NumberBox
27878  * @extends Roo.bootstrap.Component
27879  * Bootstrap NumberBox class
27880  * @cfg {String} headline Box headline
27881  * @cfg {String} content Box content
27882  * @cfg {String} icon Box icon
27883  * @cfg {String} footer Footer text
27884  * @cfg {String} fhref Footer href
27885  * 
27886  * @constructor
27887  * Create a new NumberBox
27888  * @param {Object} config The config object
27889  */
27890
27891
27892 Roo.bootstrap.dash.NumberBox = function(config){
27893     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27894     
27895 };
27896
27897 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27898     
27899     headline : '',
27900     content : '',
27901     icon : '',
27902     footer : '',
27903     fhref : '',
27904     ficon : '',
27905     
27906     getAutoCreate : function(){
27907         
27908         var cfg = {
27909             tag : 'div',
27910             cls : 'small-box ',
27911             cn : [
27912                 {
27913                     tag : 'div',
27914                     cls : 'inner',
27915                     cn :[
27916                         {
27917                             tag : 'h3',
27918                             cls : 'roo-headline',
27919                             html : this.headline
27920                         },
27921                         {
27922                             tag : 'p',
27923                             cls : 'roo-content',
27924                             html : this.content
27925                         }
27926                     ]
27927                 }
27928             ]
27929         };
27930         
27931         if(this.icon){
27932             cfg.cn.push({
27933                 tag : 'div',
27934                 cls : 'icon',
27935                 cn :[
27936                     {
27937                         tag : 'i',
27938                         cls : 'ion ' + this.icon
27939                     }
27940                 ]
27941             });
27942         }
27943         
27944         if(this.footer){
27945             var footer = {
27946                 tag : 'a',
27947                 cls : 'small-box-footer',
27948                 href : this.fhref || '#',
27949                 html : this.footer
27950             };
27951             
27952             cfg.cn.push(footer);
27953             
27954         }
27955         
27956         return  cfg;
27957     },
27958
27959     onRender : function(ct,position){
27960         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27961
27962
27963        
27964                 
27965     },
27966
27967     setHeadline: function (value)
27968     {
27969         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27970     },
27971     
27972     setFooter: function (value, href)
27973     {
27974         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27975         
27976         if(href){
27977             this.el.select('a.small-box-footer',true).first().attr('href', href);
27978         }
27979         
27980     },
27981
27982     setContent: function (value)
27983     {
27984         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27985     },
27986
27987     initEvents: function() 
27988     {   
27989         
27990     }
27991     
27992 });
27993
27994  
27995 /*
27996  * - LGPL
27997  *
27998  * TabBox
27999  * 
28000  */
28001 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28002
28003 /**
28004  * @class Roo.bootstrap.dash.TabBox
28005  * @extends Roo.bootstrap.Component
28006  * Bootstrap TabBox class
28007  * @cfg {String} title Title of the TabBox
28008  * @cfg {String} icon Icon of the TabBox
28009  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28010  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28011  * 
28012  * @constructor
28013  * Create a new TabBox
28014  * @param {Object} config The config object
28015  */
28016
28017
28018 Roo.bootstrap.dash.TabBox = function(config){
28019     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28020     this.addEvents({
28021         // raw events
28022         /**
28023          * @event addpane
28024          * When a pane is added
28025          * @param {Roo.bootstrap.dash.TabPane} pane
28026          */
28027         "addpane" : true,
28028         /**
28029          * @event activatepane
28030          * When a pane is activated
28031          * @param {Roo.bootstrap.dash.TabPane} pane
28032          */
28033         "activatepane" : true
28034         
28035          
28036     });
28037     
28038     this.panes = [];
28039 };
28040
28041 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28042
28043     title : '',
28044     icon : false,
28045     showtabs : true,
28046     tabScrollable : false,
28047     
28048     getChildContainer : function()
28049     {
28050         return this.el.select('.tab-content', true).first();
28051     },
28052     
28053     getAutoCreate : function(){
28054         
28055         var header = {
28056             tag: 'li',
28057             cls: 'pull-left header',
28058             html: this.title,
28059             cn : []
28060         };
28061         
28062         if(this.icon){
28063             header.cn.push({
28064                 tag: 'i',
28065                 cls: 'fa ' + this.icon
28066             });
28067         }
28068         
28069         var h = {
28070             tag: 'ul',
28071             cls: 'nav nav-tabs pull-right',
28072             cn: [
28073                 header
28074             ]
28075         };
28076         
28077         if(this.tabScrollable){
28078             h = {
28079                 tag: 'div',
28080                 cls: 'tab-header',
28081                 cn: [
28082                     {
28083                         tag: 'ul',
28084                         cls: 'nav nav-tabs pull-right',
28085                         cn: [
28086                             header
28087                         ]
28088                     }
28089                 ]
28090             };
28091         }
28092         
28093         var cfg = {
28094             tag: 'div',
28095             cls: 'nav-tabs-custom',
28096             cn: [
28097                 h,
28098                 {
28099                     tag: 'div',
28100                     cls: 'tab-content no-padding',
28101                     cn: []
28102                 }
28103             ]
28104         };
28105
28106         return  cfg;
28107     },
28108     initEvents : function()
28109     {
28110         //Roo.log('add add pane handler');
28111         this.on('addpane', this.onAddPane, this);
28112     },
28113      /**
28114      * Updates the box title
28115      * @param {String} html to set the title to.
28116      */
28117     setTitle : function(value)
28118     {
28119         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28120     },
28121     onAddPane : function(pane)
28122     {
28123         this.panes.push(pane);
28124         //Roo.log('addpane');
28125         //Roo.log(pane);
28126         // tabs are rendere left to right..
28127         if(!this.showtabs){
28128             return;
28129         }
28130         
28131         var ctr = this.el.select('.nav-tabs', true).first();
28132          
28133          
28134         var existing = ctr.select('.nav-tab',true);
28135         var qty = existing.getCount();;
28136         
28137         
28138         var tab = ctr.createChild({
28139             tag : 'li',
28140             cls : 'nav-tab' + (qty ? '' : ' active'),
28141             cn : [
28142                 {
28143                     tag : 'a',
28144                     href:'#',
28145                     html : pane.title
28146                 }
28147             ]
28148         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28149         pane.tab = tab;
28150         
28151         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28152         if (!qty) {
28153             pane.el.addClass('active');
28154         }
28155         
28156                 
28157     },
28158     onTabClick : function(ev,un,ob,pane)
28159     {
28160         //Roo.log('tab - prev default');
28161         ev.preventDefault();
28162         
28163         
28164         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28165         pane.tab.addClass('active');
28166         //Roo.log(pane.title);
28167         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28168         // technically we should have a deactivate event.. but maybe add later.
28169         // and it should not de-activate the selected tab...
28170         this.fireEvent('activatepane', pane);
28171         pane.el.addClass('active');
28172         pane.fireEvent('activate');
28173         
28174         
28175     },
28176     
28177     getActivePane : function()
28178     {
28179         var r = false;
28180         Roo.each(this.panes, function(p) {
28181             if(p.el.hasClass('active')){
28182                 r = p;
28183                 return false;
28184             }
28185             
28186             return;
28187         });
28188         
28189         return r;
28190     }
28191     
28192     
28193 });
28194
28195  
28196 /*
28197  * - LGPL
28198  *
28199  * Tab pane
28200  * 
28201  */
28202 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28203 /**
28204  * @class Roo.bootstrap.TabPane
28205  * @extends Roo.bootstrap.Component
28206  * Bootstrap TabPane class
28207  * @cfg {Boolean} active (false | true) Default false
28208  * @cfg {String} title title of panel
28209
28210  * 
28211  * @constructor
28212  * Create a new TabPane
28213  * @param {Object} config The config object
28214  */
28215
28216 Roo.bootstrap.dash.TabPane = function(config){
28217     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28218     
28219     this.addEvents({
28220         // raw events
28221         /**
28222          * @event activate
28223          * When a pane is activated
28224          * @param {Roo.bootstrap.dash.TabPane} pane
28225          */
28226         "activate" : true
28227          
28228     });
28229 };
28230
28231 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28232     
28233     active : false,
28234     title : '',
28235     
28236     // the tabBox that this is attached to.
28237     tab : false,
28238      
28239     getAutoCreate : function() 
28240     {
28241         var cfg = {
28242             tag: 'div',
28243             cls: 'tab-pane'
28244         };
28245         
28246         if(this.active){
28247             cfg.cls += ' active';
28248         }
28249         
28250         return cfg;
28251     },
28252     initEvents  : function()
28253     {
28254         //Roo.log('trigger add pane handler');
28255         this.parent().fireEvent('addpane', this)
28256     },
28257     
28258      /**
28259      * Updates the tab title 
28260      * @param {String} html to set the title to.
28261      */
28262     setTitle: function(str)
28263     {
28264         if (!this.tab) {
28265             return;
28266         }
28267         this.title = str;
28268         this.tab.select('a', true).first().dom.innerHTML = str;
28269         
28270     }
28271     
28272     
28273     
28274 });
28275
28276  
28277
28278
28279  /*
28280  * - LGPL
28281  *
28282  * menu
28283  * 
28284  */
28285 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28286
28287 /**
28288  * @class Roo.bootstrap.menu.Menu
28289  * @extends Roo.bootstrap.Component
28290  * Bootstrap Menu class - container for Menu
28291  * @cfg {String} html Text of the menu
28292  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28293  * @cfg {String} icon Font awesome icon
28294  * @cfg {String} pos Menu align to (top | bottom) default bottom
28295  * 
28296  * 
28297  * @constructor
28298  * Create a new Menu
28299  * @param {Object} config The config object
28300  */
28301
28302
28303 Roo.bootstrap.menu.Menu = function(config){
28304     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28305     
28306     this.addEvents({
28307         /**
28308          * @event beforeshow
28309          * Fires before this menu is displayed
28310          * @param {Roo.bootstrap.menu.Menu} this
28311          */
28312         beforeshow : true,
28313         /**
28314          * @event beforehide
28315          * Fires before this menu is hidden
28316          * @param {Roo.bootstrap.menu.Menu} this
28317          */
28318         beforehide : true,
28319         /**
28320          * @event show
28321          * Fires after this menu is displayed
28322          * @param {Roo.bootstrap.menu.Menu} this
28323          */
28324         show : true,
28325         /**
28326          * @event hide
28327          * Fires after this menu is hidden
28328          * @param {Roo.bootstrap.menu.Menu} this
28329          */
28330         hide : true,
28331         /**
28332          * @event click
28333          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28334          * @param {Roo.bootstrap.menu.Menu} this
28335          * @param {Roo.EventObject} e
28336          */
28337         click : true
28338     });
28339     
28340 };
28341
28342 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28343     
28344     submenu : false,
28345     html : '',
28346     weight : 'default',
28347     icon : false,
28348     pos : 'bottom',
28349     
28350     
28351     getChildContainer : function() {
28352         if(this.isSubMenu){
28353             return this.el;
28354         }
28355         
28356         return this.el.select('ul.dropdown-menu', true).first();  
28357     },
28358     
28359     getAutoCreate : function()
28360     {
28361         var text = [
28362             {
28363                 tag : 'span',
28364                 cls : 'roo-menu-text',
28365                 html : this.html
28366             }
28367         ];
28368         
28369         if(this.icon){
28370             text.unshift({
28371                 tag : 'i',
28372                 cls : 'fa ' + this.icon
28373             })
28374         }
28375         
28376         
28377         var cfg = {
28378             tag : 'div',
28379             cls : 'btn-group',
28380             cn : [
28381                 {
28382                     tag : 'button',
28383                     cls : 'dropdown-button btn btn-' + this.weight,
28384                     cn : text
28385                 },
28386                 {
28387                     tag : 'button',
28388                     cls : 'dropdown-toggle btn btn-' + this.weight,
28389                     cn : [
28390                         {
28391                             tag : 'span',
28392                             cls : 'caret'
28393                         }
28394                     ]
28395                 },
28396                 {
28397                     tag : 'ul',
28398                     cls : 'dropdown-menu'
28399                 }
28400             ]
28401             
28402         };
28403         
28404         if(this.pos == 'top'){
28405             cfg.cls += ' dropup';
28406         }
28407         
28408         if(this.isSubMenu){
28409             cfg = {
28410                 tag : 'ul',
28411                 cls : 'dropdown-menu'
28412             }
28413         }
28414         
28415         return cfg;
28416     },
28417     
28418     onRender : function(ct, position)
28419     {
28420         this.isSubMenu = ct.hasClass('dropdown-submenu');
28421         
28422         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28423     },
28424     
28425     initEvents : function() 
28426     {
28427         if(this.isSubMenu){
28428             return;
28429         }
28430         
28431         this.hidden = true;
28432         
28433         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28434         this.triggerEl.on('click', this.onTriggerPress, this);
28435         
28436         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28437         this.buttonEl.on('click', this.onClick, this);
28438         
28439     },
28440     
28441     list : function()
28442     {
28443         if(this.isSubMenu){
28444             return this.el;
28445         }
28446         
28447         return this.el.select('ul.dropdown-menu', true).first();
28448     },
28449     
28450     onClick : function(e)
28451     {
28452         this.fireEvent("click", this, e);
28453     },
28454     
28455     onTriggerPress  : function(e)
28456     {   
28457         if (this.isVisible()) {
28458             this.hide();
28459         } else {
28460             this.show();
28461         }
28462     },
28463     
28464     isVisible : function(){
28465         return !this.hidden;
28466     },
28467     
28468     show : function()
28469     {
28470         this.fireEvent("beforeshow", this);
28471         
28472         this.hidden = false;
28473         this.el.addClass('open');
28474         
28475         Roo.get(document).on("mouseup", this.onMouseUp, this);
28476         
28477         this.fireEvent("show", this);
28478         
28479         
28480     },
28481     
28482     hide : function()
28483     {
28484         this.fireEvent("beforehide", this);
28485         
28486         this.hidden = true;
28487         this.el.removeClass('open');
28488         
28489         Roo.get(document).un("mouseup", this.onMouseUp);
28490         
28491         this.fireEvent("hide", this);
28492     },
28493     
28494     onMouseUp : function()
28495     {
28496         this.hide();
28497     }
28498     
28499 });
28500
28501  
28502  /*
28503  * - LGPL
28504  *
28505  * menu item
28506  * 
28507  */
28508 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28509
28510 /**
28511  * @class Roo.bootstrap.menu.Item
28512  * @extends Roo.bootstrap.Component
28513  * Bootstrap MenuItem class
28514  * @cfg {Boolean} submenu (true | false) default false
28515  * @cfg {String} html text of the item
28516  * @cfg {String} href the link
28517  * @cfg {Boolean} disable (true | false) default false
28518  * @cfg {Boolean} preventDefault (true | false) default true
28519  * @cfg {String} icon Font awesome icon
28520  * @cfg {String} pos Submenu align to (left | right) default right 
28521  * 
28522  * 
28523  * @constructor
28524  * Create a new Item
28525  * @param {Object} config The config object
28526  */
28527
28528
28529 Roo.bootstrap.menu.Item = function(config){
28530     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28531     this.addEvents({
28532         /**
28533          * @event mouseover
28534          * Fires when the mouse is hovering over this menu
28535          * @param {Roo.bootstrap.menu.Item} this
28536          * @param {Roo.EventObject} e
28537          */
28538         mouseover : true,
28539         /**
28540          * @event mouseout
28541          * Fires when the mouse exits this menu
28542          * @param {Roo.bootstrap.menu.Item} this
28543          * @param {Roo.EventObject} e
28544          */
28545         mouseout : true,
28546         // raw events
28547         /**
28548          * @event click
28549          * The raw click event for the entire grid.
28550          * @param {Roo.EventObject} e
28551          */
28552         click : true
28553     });
28554 };
28555
28556 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28557     
28558     submenu : false,
28559     href : '',
28560     html : '',
28561     preventDefault: true,
28562     disable : false,
28563     icon : false,
28564     pos : 'right',
28565     
28566     getAutoCreate : function()
28567     {
28568         var text = [
28569             {
28570                 tag : 'span',
28571                 cls : 'roo-menu-item-text',
28572                 html : this.html
28573             }
28574         ];
28575         
28576         if(this.icon){
28577             text.unshift({
28578                 tag : 'i',
28579                 cls : 'fa ' + this.icon
28580             })
28581         }
28582         
28583         var cfg = {
28584             tag : 'li',
28585             cn : [
28586                 {
28587                     tag : 'a',
28588                     href : this.href || '#',
28589                     cn : text
28590                 }
28591             ]
28592         };
28593         
28594         if(this.disable){
28595             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28596         }
28597         
28598         if(this.submenu){
28599             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28600             
28601             if(this.pos == 'left'){
28602                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28603             }
28604         }
28605         
28606         return cfg;
28607     },
28608     
28609     initEvents : function() 
28610     {
28611         this.el.on('mouseover', this.onMouseOver, this);
28612         this.el.on('mouseout', this.onMouseOut, this);
28613         
28614         this.el.select('a', true).first().on('click', this.onClick, this);
28615         
28616     },
28617     
28618     onClick : function(e)
28619     {
28620         if(this.preventDefault){
28621             e.preventDefault();
28622         }
28623         
28624         this.fireEvent("click", this, e);
28625     },
28626     
28627     onMouseOver : function(e)
28628     {
28629         if(this.submenu && this.pos == 'left'){
28630             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28631         }
28632         
28633         this.fireEvent("mouseover", this, e);
28634     },
28635     
28636     onMouseOut : function(e)
28637     {
28638         this.fireEvent("mouseout", this, e);
28639     }
28640 });
28641
28642  
28643
28644  /*
28645  * - LGPL
28646  *
28647  * menu separator
28648  * 
28649  */
28650 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28651
28652 /**
28653  * @class Roo.bootstrap.menu.Separator
28654  * @extends Roo.bootstrap.Component
28655  * Bootstrap Separator class
28656  * 
28657  * @constructor
28658  * Create a new Separator
28659  * @param {Object} config The config object
28660  */
28661
28662
28663 Roo.bootstrap.menu.Separator = function(config){
28664     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28665 };
28666
28667 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28668     
28669     getAutoCreate : function(){
28670         var cfg = {
28671             tag : 'li',
28672             cls: 'divider'
28673         };
28674         
28675         return cfg;
28676     }
28677    
28678 });
28679
28680  
28681
28682  /*
28683  * - LGPL
28684  *
28685  * Tooltip
28686  * 
28687  */
28688
28689 /**
28690  * @class Roo.bootstrap.Tooltip
28691  * Bootstrap Tooltip class
28692  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28693  * to determine which dom element triggers the tooltip.
28694  * 
28695  * It needs to add support for additional attributes like tooltip-position
28696  * 
28697  * @constructor
28698  * Create a new Toolti
28699  * @param {Object} config The config object
28700  */
28701
28702 Roo.bootstrap.Tooltip = function(config){
28703     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28704     
28705     this.alignment = Roo.bootstrap.Tooltip.alignment;
28706     
28707     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28708         this.alignment = config.alignment;
28709     }
28710     
28711 };
28712
28713 Roo.apply(Roo.bootstrap.Tooltip, {
28714     /**
28715      * @function init initialize tooltip monitoring.
28716      * @static
28717      */
28718     currentEl : false,
28719     currentTip : false,
28720     currentRegion : false,
28721     
28722     //  init : delay?
28723     
28724     init : function()
28725     {
28726         Roo.get(document).on('mouseover', this.enter ,this);
28727         Roo.get(document).on('mouseout', this.leave, this);
28728          
28729         
28730         this.currentTip = new Roo.bootstrap.Tooltip();
28731     },
28732     
28733     enter : function(ev)
28734     {
28735         var dom = ev.getTarget();
28736         
28737         //Roo.log(['enter',dom]);
28738         var el = Roo.fly(dom);
28739         if (this.currentEl) {
28740             //Roo.log(dom);
28741             //Roo.log(this.currentEl);
28742             //Roo.log(this.currentEl.contains(dom));
28743             if (this.currentEl == el) {
28744                 return;
28745             }
28746             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28747                 return;
28748             }
28749
28750         }
28751         
28752         if (this.currentTip.el) {
28753             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28754         }    
28755         //Roo.log(ev);
28756         
28757         if(!el || el.dom == document){
28758             return;
28759         }
28760         
28761         var bindEl = el;
28762         
28763         // you can not look for children, as if el is the body.. then everythign is the child..
28764         if (!el.attr('tooltip')) { //
28765             if (!el.select("[tooltip]").elements.length) {
28766                 return;
28767             }
28768             // is the mouse over this child...?
28769             bindEl = el.select("[tooltip]").first();
28770             var xy = ev.getXY();
28771             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28772                 //Roo.log("not in region.");
28773                 return;
28774             }
28775             //Roo.log("child element over..");
28776             
28777         }
28778         this.currentEl = bindEl;
28779         this.currentTip.bind(bindEl);
28780         this.currentRegion = Roo.lib.Region.getRegion(dom);
28781         this.currentTip.enter();
28782         
28783     },
28784     leave : function(ev)
28785     {
28786         var dom = ev.getTarget();
28787         //Roo.log(['leave',dom]);
28788         if (!this.currentEl) {
28789             return;
28790         }
28791         
28792         
28793         if (dom != this.currentEl.dom) {
28794             return;
28795         }
28796         var xy = ev.getXY();
28797         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28798             return;
28799         }
28800         // only activate leave if mouse cursor is outside... bounding box..
28801         
28802         
28803         
28804         
28805         if (this.currentTip) {
28806             this.currentTip.leave();
28807         }
28808         //Roo.log('clear currentEl');
28809         this.currentEl = false;
28810         
28811         
28812     },
28813     alignment : {
28814         'left' : ['r-l', [-2,0], 'right'],
28815         'right' : ['l-r', [2,0], 'left'],
28816         'bottom' : ['t-b', [0,2], 'top'],
28817         'top' : [ 'b-t', [0,-2], 'bottom']
28818     }
28819     
28820 });
28821
28822
28823 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28824     
28825     
28826     bindEl : false,
28827     
28828     delay : null, // can be { show : 300 , hide: 500}
28829     
28830     timeout : null,
28831     
28832     hoverState : null, //???
28833     
28834     placement : 'bottom', 
28835     
28836     alignment : false,
28837     
28838     getAutoCreate : function(){
28839     
28840         var cfg = {
28841            cls : 'tooltip',   
28842            role : 'tooltip',
28843            cn : [
28844                 {
28845                     cls : 'tooltip-arrow arrow'
28846                 },
28847                 {
28848                     cls : 'tooltip-inner'
28849                 }
28850            ]
28851         };
28852         
28853         return cfg;
28854     },
28855     bind : function(el)
28856     {
28857         this.bindEl = el;
28858     },
28859     
28860     initEvents : function()
28861     {
28862         this.arrowEl = this.el.select('.arrow', true).first();
28863         this.innerEl = this.el.select('.tooltip-inner', true).first();
28864     },
28865     
28866     enter : function () {
28867        
28868         if (this.timeout != null) {
28869             clearTimeout(this.timeout);
28870         }
28871         
28872         this.hoverState = 'in';
28873          //Roo.log("enter - show");
28874         if (!this.delay || !this.delay.show) {
28875             this.show();
28876             return;
28877         }
28878         var _t = this;
28879         this.timeout = setTimeout(function () {
28880             if (_t.hoverState == 'in') {
28881                 _t.show();
28882             }
28883         }, this.delay.show);
28884     },
28885     leave : function()
28886     {
28887         clearTimeout(this.timeout);
28888     
28889         this.hoverState = 'out';
28890          if (!this.delay || !this.delay.hide) {
28891             this.hide();
28892             return;
28893         }
28894        
28895         var _t = this;
28896         this.timeout = setTimeout(function () {
28897             //Roo.log("leave - timeout");
28898             
28899             if (_t.hoverState == 'out') {
28900                 _t.hide();
28901                 Roo.bootstrap.Tooltip.currentEl = false;
28902             }
28903         }, delay);
28904     },
28905     
28906     show : function (msg)
28907     {
28908         if (!this.el) {
28909             this.render(document.body);
28910         }
28911         // set content.
28912         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28913         
28914         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28915         
28916         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28917         
28918         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28919                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28920         
28921         var placement = typeof this.placement == 'function' ?
28922             this.placement.call(this, this.el, on_el) :
28923             this.placement;
28924             
28925         var autoToken = /\s?auto?\s?/i;
28926         var autoPlace = autoToken.test(placement);
28927         if (autoPlace) {
28928             placement = placement.replace(autoToken, '') || 'top';
28929         }
28930         
28931         //this.el.detach()
28932         //this.el.setXY([0,0]);
28933         this.el.show();
28934         //this.el.dom.style.display='block';
28935         
28936         //this.el.appendTo(on_el);
28937         
28938         var p = this.getPosition();
28939         var box = this.el.getBox();
28940         
28941         if (autoPlace) {
28942             // fixme..
28943         }
28944         
28945         var align = this.alignment[placement];
28946         
28947         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28948         
28949         if(placement == 'top' || placement == 'bottom'){
28950             if(xy[0] < 0){
28951                 placement = 'right';
28952             }
28953             
28954             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28955                 placement = 'left';
28956             }
28957             
28958             var scroll = Roo.select('body', true).first().getScroll();
28959             
28960             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28961                 placement = 'top';
28962             }
28963             
28964             align = this.alignment[placement];
28965             
28966             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28967             
28968         }
28969         
28970         this.el.alignTo(this.bindEl, align[0],align[1]);
28971         //var arrow = this.el.select('.arrow',true).first();
28972         //arrow.set(align[2], 
28973         
28974         this.el.addClass(placement);
28975         this.el.addClass("bs-tooltip-"+ placement);
28976         
28977         this.el.addClass('in fade show');
28978         
28979         this.hoverState = null;
28980         
28981         if (this.el.hasClass('fade')) {
28982             // fade it?
28983         }
28984         
28985         
28986         
28987         
28988         
28989     },
28990     hide : function()
28991     {
28992          
28993         if (!this.el) {
28994             return;
28995         }
28996         //this.el.setXY([0,0]);
28997         this.el.removeClass(['show', 'in']);
28998         //this.el.hide();
28999         
29000     }
29001     
29002 });
29003  
29004
29005  /*
29006  * - LGPL
29007  *
29008  * Location Picker
29009  * 
29010  */
29011
29012 /**
29013  * @class Roo.bootstrap.LocationPicker
29014  * @extends Roo.bootstrap.Component
29015  * Bootstrap LocationPicker class
29016  * @cfg {Number} latitude Position when init default 0
29017  * @cfg {Number} longitude Position when init default 0
29018  * @cfg {Number} zoom default 15
29019  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29020  * @cfg {Boolean} mapTypeControl default false
29021  * @cfg {Boolean} disableDoubleClickZoom default false
29022  * @cfg {Boolean} scrollwheel default true
29023  * @cfg {Boolean} streetViewControl default false
29024  * @cfg {Number} radius default 0
29025  * @cfg {String} locationName
29026  * @cfg {Boolean} draggable default true
29027  * @cfg {Boolean} enableAutocomplete default false
29028  * @cfg {Boolean} enableReverseGeocode default true
29029  * @cfg {String} markerTitle
29030  * 
29031  * @constructor
29032  * Create a new LocationPicker
29033  * @param {Object} config The config object
29034  */
29035
29036
29037 Roo.bootstrap.LocationPicker = function(config){
29038     
29039     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29040     
29041     this.addEvents({
29042         /**
29043          * @event initial
29044          * Fires when the picker initialized.
29045          * @param {Roo.bootstrap.LocationPicker} this
29046          * @param {Google Location} location
29047          */
29048         initial : true,
29049         /**
29050          * @event positionchanged
29051          * Fires when the picker position changed.
29052          * @param {Roo.bootstrap.LocationPicker} this
29053          * @param {Google Location} location
29054          */
29055         positionchanged : true,
29056         /**
29057          * @event resize
29058          * Fires when the map resize.
29059          * @param {Roo.bootstrap.LocationPicker} this
29060          */
29061         resize : true,
29062         /**
29063          * @event show
29064          * Fires when the map show.
29065          * @param {Roo.bootstrap.LocationPicker} this
29066          */
29067         show : true,
29068         /**
29069          * @event hide
29070          * Fires when the map hide.
29071          * @param {Roo.bootstrap.LocationPicker} this
29072          */
29073         hide : true,
29074         /**
29075          * @event mapClick
29076          * Fires when click the map.
29077          * @param {Roo.bootstrap.LocationPicker} this
29078          * @param {Map event} e
29079          */
29080         mapClick : true,
29081         /**
29082          * @event mapRightClick
29083          * Fires when right click the map.
29084          * @param {Roo.bootstrap.LocationPicker} this
29085          * @param {Map event} e
29086          */
29087         mapRightClick : true,
29088         /**
29089          * @event markerClick
29090          * Fires when click the marker.
29091          * @param {Roo.bootstrap.LocationPicker} this
29092          * @param {Map event} e
29093          */
29094         markerClick : true,
29095         /**
29096          * @event markerRightClick
29097          * Fires when right click the marker.
29098          * @param {Roo.bootstrap.LocationPicker} this
29099          * @param {Map event} e
29100          */
29101         markerRightClick : true,
29102         /**
29103          * @event OverlayViewDraw
29104          * Fires when OverlayView Draw
29105          * @param {Roo.bootstrap.LocationPicker} this
29106          */
29107         OverlayViewDraw : true,
29108         /**
29109          * @event OverlayViewOnAdd
29110          * Fires when OverlayView Draw
29111          * @param {Roo.bootstrap.LocationPicker} this
29112          */
29113         OverlayViewOnAdd : true,
29114         /**
29115          * @event OverlayViewOnRemove
29116          * Fires when OverlayView Draw
29117          * @param {Roo.bootstrap.LocationPicker} this
29118          */
29119         OverlayViewOnRemove : true,
29120         /**
29121          * @event OverlayViewShow
29122          * Fires when OverlayView Draw
29123          * @param {Roo.bootstrap.LocationPicker} this
29124          * @param {Pixel} cpx
29125          */
29126         OverlayViewShow : true,
29127         /**
29128          * @event OverlayViewHide
29129          * Fires when OverlayView Draw
29130          * @param {Roo.bootstrap.LocationPicker} this
29131          */
29132         OverlayViewHide : true,
29133         /**
29134          * @event loadexception
29135          * Fires when load google lib failed.
29136          * @param {Roo.bootstrap.LocationPicker} this
29137          */
29138         loadexception : true
29139     });
29140         
29141 };
29142
29143 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29144     
29145     gMapContext: false,
29146     
29147     latitude: 0,
29148     longitude: 0,
29149     zoom: 15,
29150     mapTypeId: false,
29151     mapTypeControl: false,
29152     disableDoubleClickZoom: false,
29153     scrollwheel: true,
29154     streetViewControl: false,
29155     radius: 0,
29156     locationName: '',
29157     draggable: true,
29158     enableAutocomplete: false,
29159     enableReverseGeocode: true,
29160     markerTitle: '',
29161     
29162     getAutoCreate: function()
29163     {
29164
29165         var cfg = {
29166             tag: 'div',
29167             cls: 'roo-location-picker'
29168         };
29169         
29170         return cfg
29171     },
29172     
29173     initEvents: function(ct, position)
29174     {       
29175         if(!this.el.getWidth() || this.isApplied()){
29176             return;
29177         }
29178         
29179         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29180         
29181         this.initial();
29182     },
29183     
29184     initial: function()
29185     {
29186         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29187             this.fireEvent('loadexception', this);
29188             return;
29189         }
29190         
29191         if(!this.mapTypeId){
29192             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29193         }
29194         
29195         this.gMapContext = this.GMapContext();
29196         
29197         this.initOverlayView();
29198         
29199         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29200         
29201         var _this = this;
29202                 
29203         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29204             _this.setPosition(_this.gMapContext.marker.position);
29205         });
29206         
29207         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29208             _this.fireEvent('mapClick', this, event);
29209             
29210         });
29211
29212         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29213             _this.fireEvent('mapRightClick', this, event);
29214             
29215         });
29216         
29217         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29218             _this.fireEvent('markerClick', this, event);
29219             
29220         });
29221
29222         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29223             _this.fireEvent('markerRightClick', this, event);
29224             
29225         });
29226         
29227         this.setPosition(this.gMapContext.location);
29228         
29229         this.fireEvent('initial', this, this.gMapContext.location);
29230     },
29231     
29232     initOverlayView: function()
29233     {
29234         var _this = this;
29235         
29236         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29237             
29238             draw: function()
29239             {
29240                 _this.fireEvent('OverlayViewDraw', _this);
29241             },
29242             
29243             onAdd: function()
29244             {
29245                 _this.fireEvent('OverlayViewOnAdd', _this);
29246             },
29247             
29248             onRemove: function()
29249             {
29250                 _this.fireEvent('OverlayViewOnRemove', _this);
29251             },
29252             
29253             show: function(cpx)
29254             {
29255                 _this.fireEvent('OverlayViewShow', _this, cpx);
29256             },
29257             
29258             hide: function()
29259             {
29260                 _this.fireEvent('OverlayViewHide', _this);
29261             }
29262             
29263         });
29264     },
29265     
29266     fromLatLngToContainerPixel: function(event)
29267     {
29268         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29269     },
29270     
29271     isApplied: function() 
29272     {
29273         return this.getGmapContext() == false ? false : true;
29274     },
29275     
29276     getGmapContext: function() 
29277     {
29278         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29279     },
29280     
29281     GMapContext: function() 
29282     {
29283         var position = new google.maps.LatLng(this.latitude, this.longitude);
29284         
29285         var _map = new google.maps.Map(this.el.dom, {
29286             center: position,
29287             zoom: this.zoom,
29288             mapTypeId: this.mapTypeId,
29289             mapTypeControl: this.mapTypeControl,
29290             disableDoubleClickZoom: this.disableDoubleClickZoom,
29291             scrollwheel: this.scrollwheel,
29292             streetViewControl: this.streetViewControl,
29293             locationName: this.locationName,
29294             draggable: this.draggable,
29295             enableAutocomplete: this.enableAutocomplete,
29296             enableReverseGeocode: this.enableReverseGeocode
29297         });
29298         
29299         var _marker = new google.maps.Marker({
29300             position: position,
29301             map: _map,
29302             title: this.markerTitle,
29303             draggable: this.draggable
29304         });
29305         
29306         return {
29307             map: _map,
29308             marker: _marker,
29309             circle: null,
29310             location: position,
29311             radius: this.radius,
29312             locationName: this.locationName,
29313             addressComponents: {
29314                 formatted_address: null,
29315                 addressLine1: null,
29316                 addressLine2: null,
29317                 streetName: null,
29318                 streetNumber: null,
29319                 city: null,
29320                 district: null,
29321                 state: null,
29322                 stateOrProvince: null
29323             },
29324             settings: this,
29325             domContainer: this.el.dom,
29326             geodecoder: new google.maps.Geocoder()
29327         };
29328     },
29329     
29330     drawCircle: function(center, radius, options) 
29331     {
29332         if (this.gMapContext.circle != null) {
29333             this.gMapContext.circle.setMap(null);
29334         }
29335         if (radius > 0) {
29336             radius *= 1;
29337             options = Roo.apply({}, options, {
29338                 strokeColor: "#0000FF",
29339                 strokeOpacity: .35,
29340                 strokeWeight: 2,
29341                 fillColor: "#0000FF",
29342                 fillOpacity: .2
29343             });
29344             
29345             options.map = this.gMapContext.map;
29346             options.radius = radius;
29347             options.center = center;
29348             this.gMapContext.circle = new google.maps.Circle(options);
29349             return this.gMapContext.circle;
29350         }
29351         
29352         return null;
29353     },
29354     
29355     setPosition: function(location) 
29356     {
29357         this.gMapContext.location = location;
29358         this.gMapContext.marker.setPosition(location);
29359         this.gMapContext.map.panTo(location);
29360         this.drawCircle(location, this.gMapContext.radius, {});
29361         
29362         var _this = this;
29363         
29364         if (this.gMapContext.settings.enableReverseGeocode) {
29365             this.gMapContext.geodecoder.geocode({
29366                 latLng: this.gMapContext.location
29367             }, function(results, status) {
29368                 
29369                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29370                     _this.gMapContext.locationName = results[0].formatted_address;
29371                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29372                     
29373                     _this.fireEvent('positionchanged', this, location);
29374                 }
29375             });
29376             
29377             return;
29378         }
29379         
29380         this.fireEvent('positionchanged', this, location);
29381     },
29382     
29383     resize: function()
29384     {
29385         google.maps.event.trigger(this.gMapContext.map, "resize");
29386         
29387         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29388         
29389         this.fireEvent('resize', this);
29390     },
29391     
29392     setPositionByLatLng: function(latitude, longitude)
29393     {
29394         this.setPosition(new google.maps.LatLng(latitude, longitude));
29395     },
29396     
29397     getCurrentPosition: function() 
29398     {
29399         return {
29400             latitude: this.gMapContext.location.lat(),
29401             longitude: this.gMapContext.location.lng()
29402         };
29403     },
29404     
29405     getAddressName: function() 
29406     {
29407         return this.gMapContext.locationName;
29408     },
29409     
29410     getAddressComponents: function() 
29411     {
29412         return this.gMapContext.addressComponents;
29413     },
29414     
29415     address_component_from_google_geocode: function(address_components) 
29416     {
29417         var result = {};
29418         
29419         for (var i = 0; i < address_components.length; i++) {
29420             var component = address_components[i];
29421             if (component.types.indexOf("postal_code") >= 0) {
29422                 result.postalCode = component.short_name;
29423             } else if (component.types.indexOf("street_number") >= 0) {
29424                 result.streetNumber = component.short_name;
29425             } else if (component.types.indexOf("route") >= 0) {
29426                 result.streetName = component.short_name;
29427             } else if (component.types.indexOf("neighborhood") >= 0) {
29428                 result.city = component.short_name;
29429             } else if (component.types.indexOf("locality") >= 0) {
29430                 result.city = component.short_name;
29431             } else if (component.types.indexOf("sublocality") >= 0) {
29432                 result.district = component.short_name;
29433             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29434                 result.stateOrProvince = component.short_name;
29435             } else if (component.types.indexOf("country") >= 0) {
29436                 result.country = component.short_name;
29437             }
29438         }
29439         
29440         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29441         result.addressLine2 = "";
29442         return result;
29443     },
29444     
29445     setZoomLevel: function(zoom)
29446     {
29447         this.gMapContext.map.setZoom(zoom);
29448     },
29449     
29450     show: function()
29451     {
29452         if(!this.el){
29453             return;
29454         }
29455         
29456         this.el.show();
29457         
29458         this.resize();
29459         
29460         this.fireEvent('show', this);
29461     },
29462     
29463     hide: function()
29464     {
29465         if(!this.el){
29466             return;
29467         }
29468         
29469         this.el.hide();
29470         
29471         this.fireEvent('hide', this);
29472     }
29473     
29474 });
29475
29476 Roo.apply(Roo.bootstrap.LocationPicker, {
29477     
29478     OverlayView : function(map, options)
29479     {
29480         options = options || {};
29481         
29482         this.setMap(map);
29483     }
29484     
29485     
29486 });/**
29487  * @class Roo.bootstrap.Alert
29488  * @extends Roo.bootstrap.Component
29489  * Bootstrap Alert class - shows an alert area box
29490  * eg
29491  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29492   Enter a valid email address
29493 </div>
29494  * @licence LGPL
29495  * @cfg {String} title The title of alert
29496  * @cfg {String} html The content of alert
29497  * @cfg {String} weight (  success | info | warning | danger )
29498  * @cfg {String} faicon font-awesomeicon
29499  * 
29500  * @constructor
29501  * Create a new alert
29502  * @param {Object} config The config object
29503  */
29504
29505
29506 Roo.bootstrap.Alert = function(config){
29507     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29508     
29509 };
29510
29511 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29512     
29513     title: '',
29514     html: '',
29515     weight: false,
29516     faicon: false,
29517     
29518     getAutoCreate : function()
29519     {
29520         
29521         var cfg = {
29522             tag : 'div',
29523             cls : 'alert',
29524             cn : [
29525                 {
29526                     tag : 'i',
29527                     cls : 'roo-alert-icon'
29528                     
29529                 },
29530                 {
29531                     tag : 'b',
29532                     cls : 'roo-alert-title',
29533                     html : this.title
29534                 },
29535                 {
29536                     tag : 'span',
29537                     cls : 'roo-alert-text',
29538                     html : this.html
29539                 }
29540             ]
29541         };
29542         
29543         if(this.faicon){
29544             cfg.cn[0].cls += ' fa ' + this.faicon;
29545         }
29546         
29547         if(this.weight){
29548             cfg.cls += ' alert-' + this.weight;
29549         }
29550         
29551         return cfg;
29552     },
29553     
29554     initEvents: function() 
29555     {
29556         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29557     },
29558     
29559     setTitle : function(str)
29560     {
29561         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29562     },
29563     
29564     setText : function(str)
29565     {
29566         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29567     },
29568     
29569     setWeight : function(weight)
29570     {
29571         if(this.weight){
29572             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29573         }
29574         
29575         this.weight = weight;
29576         
29577         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29578     },
29579     
29580     setIcon : function(icon)
29581     {
29582         if(this.faicon){
29583             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29584         }
29585         
29586         this.faicon = icon;
29587         
29588         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29589     },
29590     
29591     hide: function() 
29592     {
29593         this.el.hide();   
29594     },
29595     
29596     show: function() 
29597     {  
29598         this.el.show();   
29599     }
29600     
29601 });
29602
29603  
29604 /*
29605 * Licence: LGPL
29606 */
29607
29608 /**
29609  * @class Roo.bootstrap.UploadCropbox
29610  * @extends Roo.bootstrap.Component
29611  * Bootstrap UploadCropbox class
29612  * @cfg {String} emptyText show when image has been loaded
29613  * @cfg {String} rotateNotify show when image too small to rotate
29614  * @cfg {Number} errorTimeout default 3000
29615  * @cfg {Number} minWidth default 300
29616  * @cfg {Number} minHeight default 300
29617  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29618  * @cfg {Boolean} isDocument (true|false) default false
29619  * @cfg {String} url action url
29620  * @cfg {String} paramName default 'imageUpload'
29621  * @cfg {String} method default POST
29622  * @cfg {Boolean} loadMask (true|false) default true
29623  * @cfg {Boolean} loadingText default 'Loading...'
29624  * 
29625  * @constructor
29626  * Create a new UploadCropbox
29627  * @param {Object} config The config object
29628  */
29629
29630 Roo.bootstrap.UploadCropbox = function(config){
29631     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29632     
29633     this.addEvents({
29634         /**
29635          * @event beforeselectfile
29636          * Fire before select file
29637          * @param {Roo.bootstrap.UploadCropbox} this
29638          */
29639         "beforeselectfile" : true,
29640         /**
29641          * @event initial
29642          * Fire after initEvent
29643          * @param {Roo.bootstrap.UploadCropbox} this
29644          */
29645         "initial" : true,
29646         /**
29647          * @event crop
29648          * Fire after initEvent
29649          * @param {Roo.bootstrap.UploadCropbox} this
29650          * @param {String} data
29651          */
29652         "crop" : true,
29653         /**
29654          * @event prepare
29655          * Fire when preparing the file data
29656          * @param {Roo.bootstrap.UploadCropbox} this
29657          * @param {Object} file
29658          */
29659         "prepare" : true,
29660         /**
29661          * @event exception
29662          * Fire when get exception
29663          * @param {Roo.bootstrap.UploadCropbox} this
29664          * @param {XMLHttpRequest} xhr
29665          */
29666         "exception" : true,
29667         /**
29668          * @event beforeloadcanvas
29669          * Fire before load the canvas
29670          * @param {Roo.bootstrap.UploadCropbox} this
29671          * @param {String} src
29672          */
29673         "beforeloadcanvas" : true,
29674         /**
29675          * @event trash
29676          * Fire when trash image
29677          * @param {Roo.bootstrap.UploadCropbox} this
29678          */
29679         "trash" : true,
29680         /**
29681          * @event download
29682          * Fire when download the image
29683          * @param {Roo.bootstrap.UploadCropbox} this
29684          */
29685         "download" : true,
29686         /**
29687          * @event footerbuttonclick
29688          * Fire when footerbuttonclick
29689          * @param {Roo.bootstrap.UploadCropbox} this
29690          * @param {String} type
29691          */
29692         "footerbuttonclick" : true,
29693         /**
29694          * @event resize
29695          * Fire when resize
29696          * @param {Roo.bootstrap.UploadCropbox} this
29697          */
29698         "resize" : true,
29699         /**
29700          * @event rotate
29701          * Fire when rotate the image
29702          * @param {Roo.bootstrap.UploadCropbox} this
29703          * @param {String} pos
29704          */
29705         "rotate" : true,
29706         /**
29707          * @event inspect
29708          * Fire when inspect the file
29709          * @param {Roo.bootstrap.UploadCropbox} this
29710          * @param {Object} file
29711          */
29712         "inspect" : true,
29713         /**
29714          * @event upload
29715          * Fire when xhr upload the file
29716          * @param {Roo.bootstrap.UploadCropbox} this
29717          * @param {Object} data
29718          */
29719         "upload" : true,
29720         /**
29721          * @event arrange
29722          * Fire when arrange the file data
29723          * @param {Roo.bootstrap.UploadCropbox} this
29724          * @param {Object} formData
29725          */
29726         "arrange" : true
29727     });
29728     
29729     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29730 };
29731
29732 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29733     
29734     emptyText : 'Click to upload image',
29735     rotateNotify : 'Image is too small to rotate',
29736     errorTimeout : 3000,
29737     scale : 0,
29738     baseScale : 1,
29739     rotate : 0,
29740     dragable : false,
29741     pinching : false,
29742     mouseX : 0,
29743     mouseY : 0,
29744     cropData : false,
29745     minWidth : 300,
29746     minHeight : 300,
29747     file : false,
29748     exif : {},
29749     baseRotate : 1,
29750     cropType : 'image/jpeg',
29751     buttons : false,
29752     canvasLoaded : false,
29753     isDocument : false,
29754     method : 'POST',
29755     paramName : 'imageUpload',
29756     loadMask : true,
29757     loadingText : 'Loading...',
29758     maskEl : false,
29759     
29760     getAutoCreate : function()
29761     {
29762         var cfg = {
29763             tag : 'div',
29764             cls : 'roo-upload-cropbox',
29765             cn : [
29766                 {
29767                     tag : 'input',
29768                     cls : 'roo-upload-cropbox-selector',
29769                     type : 'file'
29770                 },
29771                 {
29772                     tag : 'div',
29773                     cls : 'roo-upload-cropbox-body',
29774                     style : 'cursor:pointer',
29775                     cn : [
29776                         {
29777                             tag : 'div',
29778                             cls : 'roo-upload-cropbox-preview'
29779                         },
29780                         {
29781                             tag : 'div',
29782                             cls : 'roo-upload-cropbox-thumb'
29783                         },
29784                         {
29785                             tag : 'div',
29786                             cls : 'roo-upload-cropbox-empty-notify',
29787                             html : this.emptyText
29788                         },
29789                         {
29790                             tag : 'div',
29791                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29792                             html : this.rotateNotify
29793                         }
29794                     ]
29795                 },
29796                 {
29797                     tag : 'div',
29798                     cls : 'roo-upload-cropbox-footer',
29799                     cn : {
29800                         tag : 'div',
29801                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29802                         cn : []
29803                     }
29804                 }
29805             ]
29806         };
29807         
29808         return cfg;
29809     },
29810     
29811     onRender : function(ct, position)
29812     {
29813         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29814         
29815         if (this.buttons.length) {
29816             
29817             Roo.each(this.buttons, function(bb) {
29818                 
29819                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29820                 
29821                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29822                 
29823             }, this);
29824         }
29825         
29826         if(this.loadMask){
29827             this.maskEl = this.el;
29828         }
29829     },
29830     
29831     initEvents : function()
29832     {
29833         this.urlAPI = (window.createObjectURL && window) || 
29834                                 (window.URL && URL.revokeObjectURL && URL) || 
29835                                 (window.webkitURL && webkitURL);
29836                         
29837         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29838         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29839         
29840         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29841         this.selectorEl.hide();
29842         
29843         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29844         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29845         
29846         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29847         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29848         this.thumbEl.hide();
29849         
29850         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29851         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29852         
29853         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29854         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29855         this.errorEl.hide();
29856         
29857         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29858         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29859         this.footerEl.hide();
29860         
29861         this.setThumbBoxSize();
29862         
29863         this.bind();
29864         
29865         this.resize();
29866         
29867         this.fireEvent('initial', this);
29868     },
29869
29870     bind : function()
29871     {
29872         var _this = this;
29873         
29874         window.addEventListener("resize", function() { _this.resize(); } );
29875         
29876         this.bodyEl.on('click', this.beforeSelectFile, this);
29877         
29878         if(Roo.isTouch){
29879             this.bodyEl.on('touchstart', this.onTouchStart, this);
29880             this.bodyEl.on('touchmove', this.onTouchMove, this);
29881             this.bodyEl.on('touchend', this.onTouchEnd, this);
29882         }
29883         
29884         if(!Roo.isTouch){
29885             this.bodyEl.on('mousedown', this.onMouseDown, this);
29886             this.bodyEl.on('mousemove', this.onMouseMove, this);
29887             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29888             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29889             Roo.get(document).on('mouseup', this.onMouseUp, this);
29890         }
29891         
29892         this.selectorEl.on('change', this.onFileSelected, this);
29893     },
29894     
29895     reset : function()
29896     {    
29897         this.scale = 0;
29898         this.baseScale = 1;
29899         this.rotate = 0;
29900         this.baseRotate = 1;
29901         this.dragable = false;
29902         this.pinching = false;
29903         this.mouseX = 0;
29904         this.mouseY = 0;
29905         this.cropData = false;
29906         this.notifyEl.dom.innerHTML = this.emptyText;
29907         
29908         this.selectorEl.dom.value = '';
29909         
29910     },
29911     
29912     resize : function()
29913     {
29914         if(this.fireEvent('resize', this) != false){
29915             this.setThumbBoxPosition();
29916             this.setCanvasPosition();
29917         }
29918     },
29919     
29920     onFooterButtonClick : function(e, el, o, type)
29921     {
29922         switch (type) {
29923             case 'rotate-left' :
29924                 this.onRotateLeft(e);
29925                 break;
29926             case 'rotate-right' :
29927                 this.onRotateRight(e);
29928                 break;
29929             case 'picture' :
29930                 this.beforeSelectFile(e);
29931                 break;
29932             case 'trash' :
29933                 this.trash(e);
29934                 break;
29935             case 'crop' :
29936                 this.crop(e);
29937                 break;
29938             case 'download' :
29939                 this.download(e);
29940                 break;
29941             default :
29942                 break;
29943         }
29944         
29945         this.fireEvent('footerbuttonclick', this, type);
29946     },
29947     
29948     beforeSelectFile : function(e)
29949     {
29950         e.preventDefault();
29951         
29952         if(this.fireEvent('beforeselectfile', this) != false){
29953             this.selectorEl.dom.click();
29954         }
29955     },
29956     
29957     onFileSelected : function(e)
29958     {
29959         e.preventDefault();
29960         
29961         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29962             return;
29963         }
29964         
29965         var file = this.selectorEl.dom.files[0];
29966         
29967         if(this.fireEvent('inspect', this, file) != false){
29968             this.prepare(file);
29969         }
29970         
29971     },
29972     
29973     trash : function(e)
29974     {
29975         this.fireEvent('trash', this);
29976     },
29977     
29978     download : function(e)
29979     {
29980         this.fireEvent('download', this);
29981     },
29982     
29983     loadCanvas : function(src)
29984     {   
29985         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29986             
29987             this.reset();
29988             
29989             this.imageEl = document.createElement('img');
29990             
29991             var _this = this;
29992             
29993             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29994             
29995             this.imageEl.src = src;
29996         }
29997     },
29998     
29999     onLoadCanvas : function()
30000     {   
30001         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30002         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30003         
30004         this.bodyEl.un('click', this.beforeSelectFile, this);
30005         
30006         this.notifyEl.hide();
30007         this.thumbEl.show();
30008         this.footerEl.show();
30009         
30010         this.baseRotateLevel();
30011         
30012         if(this.isDocument){
30013             this.setThumbBoxSize();
30014         }
30015         
30016         this.setThumbBoxPosition();
30017         
30018         this.baseScaleLevel();
30019         
30020         this.draw();
30021         
30022         this.resize();
30023         
30024         this.canvasLoaded = true;
30025         
30026         if(this.loadMask){
30027             this.maskEl.unmask();
30028         }
30029         
30030     },
30031     
30032     setCanvasPosition : function()
30033     {   
30034         if(!this.canvasEl){
30035             return;
30036         }
30037         
30038         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30039         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30040         
30041         this.previewEl.setLeft(pw);
30042         this.previewEl.setTop(ph);
30043         
30044     },
30045     
30046     onMouseDown : function(e)
30047     {   
30048         e.stopEvent();
30049         
30050         this.dragable = true;
30051         this.pinching = false;
30052         
30053         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30054             this.dragable = false;
30055             return;
30056         }
30057         
30058         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30059         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30060         
30061     },
30062     
30063     onMouseMove : function(e)
30064     {   
30065         e.stopEvent();
30066         
30067         if(!this.canvasLoaded){
30068             return;
30069         }
30070         
30071         if (!this.dragable){
30072             return;
30073         }
30074         
30075         var minX = Math.ceil(this.thumbEl.getLeft(true));
30076         var minY = Math.ceil(this.thumbEl.getTop(true));
30077         
30078         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30079         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30080         
30081         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30082         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30083         
30084         x = x - this.mouseX;
30085         y = y - this.mouseY;
30086         
30087         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30088         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30089         
30090         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30091         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30092         
30093         this.previewEl.setLeft(bgX);
30094         this.previewEl.setTop(bgY);
30095         
30096         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30097         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30098     },
30099     
30100     onMouseUp : function(e)
30101     {   
30102         e.stopEvent();
30103         
30104         this.dragable = false;
30105     },
30106     
30107     onMouseWheel : function(e)
30108     {   
30109         e.stopEvent();
30110         
30111         this.startScale = this.scale;
30112         
30113         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30114         
30115         if(!this.zoomable()){
30116             this.scale = this.startScale;
30117             return;
30118         }
30119         
30120         this.draw();
30121         
30122         return;
30123     },
30124     
30125     zoomable : function()
30126     {
30127         var minScale = this.thumbEl.getWidth() / this.minWidth;
30128         
30129         if(this.minWidth < this.minHeight){
30130             minScale = this.thumbEl.getHeight() / this.minHeight;
30131         }
30132         
30133         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30134         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30135         
30136         if(
30137                 this.isDocument &&
30138                 (this.rotate == 0 || this.rotate == 180) && 
30139                 (
30140                     width > this.imageEl.OriginWidth || 
30141                     height > this.imageEl.OriginHeight ||
30142                     (width < this.minWidth && height < this.minHeight)
30143                 )
30144         ){
30145             return false;
30146         }
30147         
30148         if(
30149                 this.isDocument &&
30150                 (this.rotate == 90 || this.rotate == 270) && 
30151                 (
30152                     width > this.imageEl.OriginWidth || 
30153                     height > this.imageEl.OriginHeight ||
30154                     (width < this.minHeight && height < this.minWidth)
30155                 )
30156         ){
30157             return false;
30158         }
30159         
30160         if(
30161                 !this.isDocument &&
30162                 (this.rotate == 0 || this.rotate == 180) && 
30163                 (
30164                     width < this.minWidth || 
30165                     width > this.imageEl.OriginWidth || 
30166                     height < this.minHeight || 
30167                     height > this.imageEl.OriginHeight
30168                 )
30169         ){
30170             return false;
30171         }
30172         
30173         if(
30174                 !this.isDocument &&
30175                 (this.rotate == 90 || this.rotate == 270) && 
30176                 (
30177                     width < this.minHeight || 
30178                     width > this.imageEl.OriginWidth || 
30179                     height < this.minWidth || 
30180                     height > this.imageEl.OriginHeight
30181                 )
30182         ){
30183             return false;
30184         }
30185         
30186         return true;
30187         
30188     },
30189     
30190     onRotateLeft : function(e)
30191     {   
30192         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30193             
30194             var minScale = this.thumbEl.getWidth() / this.minWidth;
30195             
30196             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30197             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30198             
30199             this.startScale = this.scale;
30200             
30201             while (this.getScaleLevel() < minScale){
30202             
30203                 this.scale = this.scale + 1;
30204                 
30205                 if(!this.zoomable()){
30206                     break;
30207                 }
30208                 
30209                 if(
30210                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30211                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30212                 ){
30213                     continue;
30214                 }
30215                 
30216                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30217
30218                 this.draw();
30219                 
30220                 return;
30221             }
30222             
30223             this.scale = this.startScale;
30224             
30225             this.onRotateFail();
30226             
30227             return false;
30228         }
30229         
30230         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30231
30232         if(this.isDocument){
30233             this.setThumbBoxSize();
30234             this.setThumbBoxPosition();
30235             this.setCanvasPosition();
30236         }
30237         
30238         this.draw();
30239         
30240         this.fireEvent('rotate', this, 'left');
30241         
30242     },
30243     
30244     onRotateRight : function(e)
30245     {
30246         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30247             
30248             var minScale = this.thumbEl.getWidth() / this.minWidth;
30249         
30250             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30251             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30252             
30253             this.startScale = this.scale;
30254             
30255             while (this.getScaleLevel() < minScale){
30256             
30257                 this.scale = this.scale + 1;
30258                 
30259                 if(!this.zoomable()){
30260                     break;
30261                 }
30262                 
30263                 if(
30264                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30265                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30266                 ){
30267                     continue;
30268                 }
30269                 
30270                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30271
30272                 this.draw();
30273                 
30274                 return;
30275             }
30276             
30277             this.scale = this.startScale;
30278             
30279             this.onRotateFail();
30280             
30281             return false;
30282         }
30283         
30284         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30285
30286         if(this.isDocument){
30287             this.setThumbBoxSize();
30288             this.setThumbBoxPosition();
30289             this.setCanvasPosition();
30290         }
30291         
30292         this.draw();
30293         
30294         this.fireEvent('rotate', this, 'right');
30295     },
30296     
30297     onRotateFail : function()
30298     {
30299         this.errorEl.show(true);
30300         
30301         var _this = this;
30302         
30303         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30304     },
30305     
30306     draw : function()
30307     {
30308         this.previewEl.dom.innerHTML = '';
30309         
30310         var canvasEl = document.createElement("canvas");
30311         
30312         var contextEl = canvasEl.getContext("2d");
30313         
30314         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30315         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30316         var center = this.imageEl.OriginWidth / 2;
30317         
30318         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30319             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30320             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30321             center = this.imageEl.OriginHeight / 2;
30322         }
30323         
30324         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30325         
30326         contextEl.translate(center, center);
30327         contextEl.rotate(this.rotate * Math.PI / 180);
30328
30329         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30330         
30331         this.canvasEl = document.createElement("canvas");
30332         
30333         this.contextEl = this.canvasEl.getContext("2d");
30334         
30335         switch (this.rotate) {
30336             case 0 :
30337                 
30338                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30339                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30340                 
30341                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30342                 
30343                 break;
30344             case 90 : 
30345                 
30346                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30347                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30348                 
30349                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30350                     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);
30351                     break;
30352                 }
30353                 
30354                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30355                 
30356                 break;
30357             case 180 :
30358                 
30359                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30360                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30361                 
30362                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30363                     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);
30364                     break;
30365                 }
30366                 
30367                 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);
30368                 
30369                 break;
30370             case 270 :
30371                 
30372                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30373                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30374         
30375                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30376                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30377                     break;
30378                 }
30379                 
30380                 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);
30381                 
30382                 break;
30383             default : 
30384                 break;
30385         }
30386         
30387         this.previewEl.appendChild(this.canvasEl);
30388         
30389         this.setCanvasPosition();
30390     },
30391     
30392     crop : function()
30393     {
30394         if(!this.canvasLoaded){
30395             return;
30396         }
30397         
30398         var imageCanvas = document.createElement("canvas");
30399         
30400         var imageContext = imageCanvas.getContext("2d");
30401         
30402         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30403         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30404         
30405         var center = imageCanvas.width / 2;
30406         
30407         imageContext.translate(center, center);
30408         
30409         imageContext.rotate(this.rotate * Math.PI / 180);
30410         
30411         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30412         
30413         var canvas = document.createElement("canvas");
30414         
30415         var context = canvas.getContext("2d");
30416                 
30417         canvas.width = this.minWidth;
30418         canvas.height = this.minHeight;
30419
30420         switch (this.rotate) {
30421             case 0 :
30422                 
30423                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30424                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30425                 
30426                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30427                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30428                 
30429                 var targetWidth = this.minWidth - 2 * x;
30430                 var targetHeight = this.minHeight - 2 * y;
30431                 
30432                 var scale = 1;
30433                 
30434                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30435                     scale = targetWidth / width;
30436                 }
30437                 
30438                 if(x > 0 && y == 0){
30439                     scale = targetHeight / height;
30440                 }
30441                 
30442                 if(x > 0 && y > 0){
30443                     scale = targetWidth / width;
30444                     
30445                     if(width < height){
30446                         scale = targetHeight / height;
30447                     }
30448                 }
30449                 
30450                 context.scale(scale, scale);
30451                 
30452                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30453                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30454
30455                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30456                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30457
30458                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30459                 
30460                 break;
30461             case 90 : 
30462                 
30463                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30464                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30465                 
30466                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30467                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30468                 
30469                 var targetWidth = this.minWidth - 2 * x;
30470                 var targetHeight = this.minHeight - 2 * y;
30471                 
30472                 var scale = 1;
30473                 
30474                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30475                     scale = targetWidth / width;
30476                 }
30477                 
30478                 if(x > 0 && y == 0){
30479                     scale = targetHeight / height;
30480                 }
30481                 
30482                 if(x > 0 && y > 0){
30483                     scale = targetWidth / width;
30484                     
30485                     if(width < height){
30486                         scale = targetHeight / height;
30487                     }
30488                 }
30489                 
30490                 context.scale(scale, scale);
30491                 
30492                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30493                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30494
30495                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30496                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30497                 
30498                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30499                 
30500                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30501                 
30502                 break;
30503             case 180 :
30504                 
30505                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30506                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30507                 
30508                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30509                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30510                 
30511                 var targetWidth = this.minWidth - 2 * x;
30512                 var targetHeight = this.minHeight - 2 * y;
30513                 
30514                 var scale = 1;
30515                 
30516                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30517                     scale = targetWidth / width;
30518                 }
30519                 
30520                 if(x > 0 && y == 0){
30521                     scale = targetHeight / height;
30522                 }
30523                 
30524                 if(x > 0 && y > 0){
30525                     scale = targetWidth / width;
30526                     
30527                     if(width < height){
30528                         scale = targetHeight / height;
30529                     }
30530                 }
30531                 
30532                 context.scale(scale, scale);
30533                 
30534                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30535                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30536
30537                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30538                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30539
30540                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30541                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30542                 
30543                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30544                 
30545                 break;
30546             case 270 :
30547                 
30548                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30549                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30550                 
30551                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30552                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30553                 
30554                 var targetWidth = this.minWidth - 2 * x;
30555                 var targetHeight = this.minHeight - 2 * y;
30556                 
30557                 var scale = 1;
30558                 
30559                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30560                     scale = targetWidth / width;
30561                 }
30562                 
30563                 if(x > 0 && y == 0){
30564                     scale = targetHeight / height;
30565                 }
30566                 
30567                 if(x > 0 && y > 0){
30568                     scale = targetWidth / width;
30569                     
30570                     if(width < height){
30571                         scale = targetHeight / height;
30572                     }
30573                 }
30574                 
30575                 context.scale(scale, scale);
30576                 
30577                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30578                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30579
30580                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30581                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30582                 
30583                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30584                 
30585                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30586                 
30587                 break;
30588             default : 
30589                 break;
30590         }
30591         
30592         this.cropData = canvas.toDataURL(this.cropType);
30593         
30594         if(this.fireEvent('crop', this, this.cropData) !== false){
30595             this.process(this.file, this.cropData);
30596         }
30597         
30598         return;
30599         
30600     },
30601     
30602     setThumbBoxSize : function()
30603     {
30604         var width, height;
30605         
30606         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30607             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30608             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30609             
30610             this.minWidth = width;
30611             this.minHeight = height;
30612             
30613             if(this.rotate == 90 || this.rotate == 270){
30614                 this.minWidth = height;
30615                 this.minHeight = width;
30616             }
30617         }
30618         
30619         height = 300;
30620         width = Math.ceil(this.minWidth * height / this.minHeight);
30621         
30622         if(this.minWidth > this.minHeight){
30623             width = 300;
30624             height = Math.ceil(this.minHeight * width / this.minWidth);
30625         }
30626         
30627         this.thumbEl.setStyle({
30628             width : width + 'px',
30629             height : height + 'px'
30630         });
30631
30632         return;
30633             
30634     },
30635     
30636     setThumbBoxPosition : function()
30637     {
30638         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30639         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30640         
30641         this.thumbEl.setLeft(x);
30642         this.thumbEl.setTop(y);
30643         
30644     },
30645     
30646     baseRotateLevel : function()
30647     {
30648         this.baseRotate = 1;
30649         
30650         if(
30651                 typeof(this.exif) != 'undefined' &&
30652                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30653                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30654         ){
30655             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30656         }
30657         
30658         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30659         
30660     },
30661     
30662     baseScaleLevel : function()
30663     {
30664         var width, height;
30665         
30666         if(this.isDocument){
30667             
30668             if(this.baseRotate == 6 || this.baseRotate == 8){
30669             
30670                 height = this.thumbEl.getHeight();
30671                 this.baseScale = height / this.imageEl.OriginWidth;
30672
30673                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30674                     width = this.thumbEl.getWidth();
30675                     this.baseScale = width / this.imageEl.OriginHeight;
30676                 }
30677
30678                 return;
30679             }
30680
30681             height = this.thumbEl.getHeight();
30682             this.baseScale = height / this.imageEl.OriginHeight;
30683
30684             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30685                 width = this.thumbEl.getWidth();
30686                 this.baseScale = width / this.imageEl.OriginWidth;
30687             }
30688
30689             return;
30690         }
30691         
30692         if(this.baseRotate == 6 || this.baseRotate == 8){
30693             
30694             width = this.thumbEl.getHeight();
30695             this.baseScale = width / this.imageEl.OriginHeight;
30696             
30697             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30698                 height = this.thumbEl.getWidth();
30699                 this.baseScale = height / this.imageEl.OriginHeight;
30700             }
30701             
30702             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30703                 height = this.thumbEl.getWidth();
30704                 this.baseScale = height / this.imageEl.OriginHeight;
30705                 
30706                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30707                     width = this.thumbEl.getHeight();
30708                     this.baseScale = width / this.imageEl.OriginWidth;
30709                 }
30710             }
30711             
30712             return;
30713         }
30714         
30715         width = this.thumbEl.getWidth();
30716         this.baseScale = width / this.imageEl.OriginWidth;
30717         
30718         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30719             height = this.thumbEl.getHeight();
30720             this.baseScale = height / this.imageEl.OriginHeight;
30721         }
30722         
30723         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30724             
30725             height = this.thumbEl.getHeight();
30726             this.baseScale = height / this.imageEl.OriginHeight;
30727             
30728             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30729                 width = this.thumbEl.getWidth();
30730                 this.baseScale = width / this.imageEl.OriginWidth;
30731             }
30732             
30733         }
30734         
30735         return;
30736     },
30737     
30738     getScaleLevel : function()
30739     {
30740         return this.baseScale * Math.pow(1.1, this.scale);
30741     },
30742     
30743     onTouchStart : function(e)
30744     {
30745         if(!this.canvasLoaded){
30746             this.beforeSelectFile(e);
30747             return;
30748         }
30749         
30750         var touches = e.browserEvent.touches;
30751         
30752         if(!touches){
30753             return;
30754         }
30755         
30756         if(touches.length == 1){
30757             this.onMouseDown(e);
30758             return;
30759         }
30760         
30761         if(touches.length != 2){
30762             return;
30763         }
30764         
30765         var coords = [];
30766         
30767         for(var i = 0, finger; finger = touches[i]; i++){
30768             coords.push(finger.pageX, finger.pageY);
30769         }
30770         
30771         var x = Math.pow(coords[0] - coords[2], 2);
30772         var y = Math.pow(coords[1] - coords[3], 2);
30773         
30774         this.startDistance = Math.sqrt(x + y);
30775         
30776         this.startScale = this.scale;
30777         
30778         this.pinching = true;
30779         this.dragable = false;
30780         
30781     },
30782     
30783     onTouchMove : function(e)
30784     {
30785         if(!this.pinching && !this.dragable){
30786             return;
30787         }
30788         
30789         var touches = e.browserEvent.touches;
30790         
30791         if(!touches){
30792             return;
30793         }
30794         
30795         if(this.dragable){
30796             this.onMouseMove(e);
30797             return;
30798         }
30799         
30800         var coords = [];
30801         
30802         for(var i = 0, finger; finger = touches[i]; i++){
30803             coords.push(finger.pageX, finger.pageY);
30804         }
30805         
30806         var x = Math.pow(coords[0] - coords[2], 2);
30807         var y = Math.pow(coords[1] - coords[3], 2);
30808         
30809         this.endDistance = Math.sqrt(x + y);
30810         
30811         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30812         
30813         if(!this.zoomable()){
30814             this.scale = this.startScale;
30815             return;
30816         }
30817         
30818         this.draw();
30819         
30820     },
30821     
30822     onTouchEnd : function(e)
30823     {
30824         this.pinching = false;
30825         this.dragable = false;
30826         
30827     },
30828     
30829     process : function(file, crop)
30830     {
30831         if(this.loadMask){
30832             this.maskEl.mask(this.loadingText);
30833         }
30834         
30835         this.xhr = new XMLHttpRequest();
30836         
30837         file.xhr = this.xhr;
30838
30839         this.xhr.open(this.method, this.url, true);
30840         
30841         var headers = {
30842             "Accept": "application/json",
30843             "Cache-Control": "no-cache",
30844             "X-Requested-With": "XMLHttpRequest"
30845         };
30846         
30847         for (var headerName in headers) {
30848             var headerValue = headers[headerName];
30849             if (headerValue) {
30850                 this.xhr.setRequestHeader(headerName, headerValue);
30851             }
30852         }
30853         
30854         var _this = this;
30855         
30856         this.xhr.onload = function()
30857         {
30858             _this.xhrOnLoad(_this.xhr);
30859         }
30860         
30861         this.xhr.onerror = function()
30862         {
30863             _this.xhrOnError(_this.xhr);
30864         }
30865         
30866         var formData = new FormData();
30867
30868         formData.append('returnHTML', 'NO');
30869         
30870         if(crop){
30871             formData.append('crop', crop);
30872         }
30873         
30874         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30875             formData.append(this.paramName, file, file.name);
30876         }
30877         
30878         if(typeof(file.filename) != 'undefined'){
30879             formData.append('filename', file.filename);
30880         }
30881         
30882         if(typeof(file.mimetype) != 'undefined'){
30883             formData.append('mimetype', file.mimetype);
30884         }
30885         
30886         if(this.fireEvent('arrange', this, formData) != false){
30887             this.xhr.send(formData);
30888         };
30889     },
30890     
30891     xhrOnLoad : function(xhr)
30892     {
30893         if(this.loadMask){
30894             this.maskEl.unmask();
30895         }
30896         
30897         if (xhr.readyState !== 4) {
30898             this.fireEvent('exception', this, xhr);
30899             return;
30900         }
30901
30902         var response = Roo.decode(xhr.responseText);
30903         
30904         if(!response.success){
30905             this.fireEvent('exception', this, xhr);
30906             return;
30907         }
30908         
30909         var response = Roo.decode(xhr.responseText);
30910         
30911         this.fireEvent('upload', this, response);
30912         
30913     },
30914     
30915     xhrOnError : function()
30916     {
30917         if(this.loadMask){
30918             this.maskEl.unmask();
30919         }
30920         
30921         Roo.log('xhr on error');
30922         
30923         var response = Roo.decode(xhr.responseText);
30924           
30925         Roo.log(response);
30926         
30927     },
30928     
30929     prepare : function(file)
30930     {   
30931         if(this.loadMask){
30932             this.maskEl.mask(this.loadingText);
30933         }
30934         
30935         this.file = false;
30936         this.exif = {};
30937         
30938         if(typeof(file) === 'string'){
30939             this.loadCanvas(file);
30940             return;
30941         }
30942         
30943         if(!file || !this.urlAPI){
30944             return;
30945         }
30946         
30947         this.file = file;
30948         this.cropType = file.type;
30949         
30950         var _this = this;
30951         
30952         if(this.fireEvent('prepare', this, this.file) != false){
30953             
30954             var reader = new FileReader();
30955             
30956             reader.onload = function (e) {
30957                 if (e.target.error) {
30958                     Roo.log(e.target.error);
30959                     return;
30960                 }
30961                 
30962                 var buffer = e.target.result,
30963                     dataView = new DataView(buffer),
30964                     offset = 2,
30965                     maxOffset = dataView.byteLength - 4,
30966                     markerBytes,
30967                     markerLength;
30968                 
30969                 if (dataView.getUint16(0) === 0xffd8) {
30970                     while (offset < maxOffset) {
30971                         markerBytes = dataView.getUint16(offset);
30972                         
30973                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30974                             markerLength = dataView.getUint16(offset + 2) + 2;
30975                             if (offset + markerLength > dataView.byteLength) {
30976                                 Roo.log('Invalid meta data: Invalid segment size.');
30977                                 break;
30978                             }
30979                             
30980                             if(markerBytes == 0xffe1){
30981                                 _this.parseExifData(
30982                                     dataView,
30983                                     offset,
30984                                     markerLength
30985                                 );
30986                             }
30987                             
30988                             offset += markerLength;
30989                             
30990                             continue;
30991                         }
30992                         
30993                         break;
30994                     }
30995                     
30996                 }
30997                 
30998                 var url = _this.urlAPI.createObjectURL(_this.file);
30999                 
31000                 _this.loadCanvas(url);
31001                 
31002                 return;
31003             }
31004             
31005             reader.readAsArrayBuffer(this.file);
31006             
31007         }
31008         
31009     },
31010     
31011     parseExifData : function(dataView, offset, length)
31012     {
31013         var tiffOffset = offset + 10,
31014             littleEndian,
31015             dirOffset;
31016     
31017         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31018             // No Exif data, might be XMP data instead
31019             return;
31020         }
31021         
31022         // Check for the ASCII code for "Exif" (0x45786966):
31023         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31024             // No Exif data, might be XMP data instead
31025             return;
31026         }
31027         if (tiffOffset + 8 > dataView.byteLength) {
31028             Roo.log('Invalid Exif data: Invalid segment size.');
31029             return;
31030         }
31031         // Check for the two null bytes:
31032         if (dataView.getUint16(offset + 8) !== 0x0000) {
31033             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31034             return;
31035         }
31036         // Check the byte alignment:
31037         switch (dataView.getUint16(tiffOffset)) {
31038         case 0x4949:
31039             littleEndian = true;
31040             break;
31041         case 0x4D4D:
31042             littleEndian = false;
31043             break;
31044         default:
31045             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31046             return;
31047         }
31048         // Check for the TIFF tag marker (0x002A):
31049         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31050             Roo.log('Invalid Exif data: Missing TIFF marker.');
31051             return;
31052         }
31053         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31054         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31055         
31056         this.parseExifTags(
31057             dataView,
31058             tiffOffset,
31059             tiffOffset + dirOffset,
31060             littleEndian
31061         );
31062     },
31063     
31064     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31065     {
31066         var tagsNumber,
31067             dirEndOffset,
31068             i;
31069         if (dirOffset + 6 > dataView.byteLength) {
31070             Roo.log('Invalid Exif data: Invalid directory offset.');
31071             return;
31072         }
31073         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31074         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31075         if (dirEndOffset + 4 > dataView.byteLength) {
31076             Roo.log('Invalid Exif data: Invalid directory size.');
31077             return;
31078         }
31079         for (i = 0; i < tagsNumber; i += 1) {
31080             this.parseExifTag(
31081                 dataView,
31082                 tiffOffset,
31083                 dirOffset + 2 + 12 * i, // tag offset
31084                 littleEndian
31085             );
31086         }
31087         // Return the offset to the next directory:
31088         return dataView.getUint32(dirEndOffset, littleEndian);
31089     },
31090     
31091     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31092     {
31093         var tag = dataView.getUint16(offset, littleEndian);
31094         
31095         this.exif[tag] = this.getExifValue(
31096             dataView,
31097             tiffOffset,
31098             offset,
31099             dataView.getUint16(offset + 2, littleEndian), // tag type
31100             dataView.getUint32(offset + 4, littleEndian), // tag length
31101             littleEndian
31102         );
31103     },
31104     
31105     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31106     {
31107         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31108             tagSize,
31109             dataOffset,
31110             values,
31111             i,
31112             str,
31113             c;
31114     
31115         if (!tagType) {
31116             Roo.log('Invalid Exif data: Invalid tag type.');
31117             return;
31118         }
31119         
31120         tagSize = tagType.size * length;
31121         // Determine if the value is contained in the dataOffset bytes,
31122         // or if the value at the dataOffset is a pointer to the actual data:
31123         dataOffset = tagSize > 4 ?
31124                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31125         if (dataOffset + tagSize > dataView.byteLength) {
31126             Roo.log('Invalid Exif data: Invalid data offset.');
31127             return;
31128         }
31129         if (length === 1) {
31130             return tagType.getValue(dataView, dataOffset, littleEndian);
31131         }
31132         values = [];
31133         for (i = 0; i < length; i += 1) {
31134             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31135         }
31136         
31137         if (tagType.ascii) {
31138             str = '';
31139             // Concatenate the chars:
31140             for (i = 0; i < values.length; i += 1) {
31141                 c = values[i];
31142                 // Ignore the terminating NULL byte(s):
31143                 if (c === '\u0000') {
31144                     break;
31145                 }
31146                 str += c;
31147             }
31148             return str;
31149         }
31150         return values;
31151     }
31152     
31153 });
31154
31155 Roo.apply(Roo.bootstrap.UploadCropbox, {
31156     tags : {
31157         'Orientation': 0x0112
31158     },
31159     
31160     Orientation: {
31161             1: 0, //'top-left',
31162 //            2: 'top-right',
31163             3: 180, //'bottom-right',
31164 //            4: 'bottom-left',
31165 //            5: 'left-top',
31166             6: 90, //'right-top',
31167 //            7: 'right-bottom',
31168             8: 270 //'left-bottom'
31169     },
31170     
31171     exifTagTypes : {
31172         // byte, 8-bit unsigned int:
31173         1: {
31174             getValue: function (dataView, dataOffset) {
31175                 return dataView.getUint8(dataOffset);
31176             },
31177             size: 1
31178         },
31179         // ascii, 8-bit byte:
31180         2: {
31181             getValue: function (dataView, dataOffset) {
31182                 return String.fromCharCode(dataView.getUint8(dataOffset));
31183             },
31184             size: 1,
31185             ascii: true
31186         },
31187         // short, 16 bit int:
31188         3: {
31189             getValue: function (dataView, dataOffset, littleEndian) {
31190                 return dataView.getUint16(dataOffset, littleEndian);
31191             },
31192             size: 2
31193         },
31194         // long, 32 bit int:
31195         4: {
31196             getValue: function (dataView, dataOffset, littleEndian) {
31197                 return dataView.getUint32(dataOffset, littleEndian);
31198             },
31199             size: 4
31200         },
31201         // rational = two long values, first is numerator, second is denominator:
31202         5: {
31203             getValue: function (dataView, dataOffset, littleEndian) {
31204                 return dataView.getUint32(dataOffset, littleEndian) /
31205                     dataView.getUint32(dataOffset + 4, littleEndian);
31206             },
31207             size: 8
31208         },
31209         // slong, 32 bit signed int:
31210         9: {
31211             getValue: function (dataView, dataOffset, littleEndian) {
31212                 return dataView.getInt32(dataOffset, littleEndian);
31213             },
31214             size: 4
31215         },
31216         // srational, two slongs, first is numerator, second is denominator:
31217         10: {
31218             getValue: function (dataView, dataOffset, littleEndian) {
31219                 return dataView.getInt32(dataOffset, littleEndian) /
31220                     dataView.getInt32(dataOffset + 4, littleEndian);
31221             },
31222             size: 8
31223         }
31224     },
31225     
31226     footer : {
31227         STANDARD : [
31228             {
31229                 tag : 'div',
31230                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31231                 action : 'rotate-left',
31232                 cn : [
31233                     {
31234                         tag : 'button',
31235                         cls : 'btn btn-default',
31236                         html : '<i class="fa fa-undo"></i>'
31237                     }
31238                 ]
31239             },
31240             {
31241                 tag : 'div',
31242                 cls : 'btn-group roo-upload-cropbox-picture',
31243                 action : 'picture',
31244                 cn : [
31245                     {
31246                         tag : 'button',
31247                         cls : 'btn btn-default',
31248                         html : '<i class="fa fa-picture-o"></i>'
31249                     }
31250                 ]
31251             },
31252             {
31253                 tag : 'div',
31254                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31255                 action : 'rotate-right',
31256                 cn : [
31257                     {
31258                         tag : 'button',
31259                         cls : 'btn btn-default',
31260                         html : '<i class="fa fa-repeat"></i>'
31261                     }
31262                 ]
31263             }
31264         ],
31265         DOCUMENT : [
31266             {
31267                 tag : 'div',
31268                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31269                 action : 'rotate-left',
31270                 cn : [
31271                     {
31272                         tag : 'button',
31273                         cls : 'btn btn-default',
31274                         html : '<i class="fa fa-undo"></i>'
31275                     }
31276                 ]
31277             },
31278             {
31279                 tag : 'div',
31280                 cls : 'btn-group roo-upload-cropbox-download',
31281                 action : 'download',
31282                 cn : [
31283                     {
31284                         tag : 'button',
31285                         cls : 'btn btn-default',
31286                         html : '<i class="fa fa-download"></i>'
31287                     }
31288                 ]
31289             },
31290             {
31291                 tag : 'div',
31292                 cls : 'btn-group roo-upload-cropbox-crop',
31293                 action : 'crop',
31294                 cn : [
31295                     {
31296                         tag : 'button',
31297                         cls : 'btn btn-default',
31298                         html : '<i class="fa fa-crop"></i>'
31299                     }
31300                 ]
31301             },
31302             {
31303                 tag : 'div',
31304                 cls : 'btn-group roo-upload-cropbox-trash',
31305                 action : 'trash',
31306                 cn : [
31307                     {
31308                         tag : 'button',
31309                         cls : 'btn btn-default',
31310                         html : '<i class="fa fa-trash"></i>'
31311                     }
31312                 ]
31313             },
31314             {
31315                 tag : 'div',
31316                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31317                 action : 'rotate-right',
31318                 cn : [
31319                     {
31320                         tag : 'button',
31321                         cls : 'btn btn-default',
31322                         html : '<i class="fa fa-repeat"></i>'
31323                     }
31324                 ]
31325             }
31326         ],
31327         ROTATOR : [
31328             {
31329                 tag : 'div',
31330                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31331                 action : 'rotate-left',
31332                 cn : [
31333                     {
31334                         tag : 'button',
31335                         cls : 'btn btn-default',
31336                         html : '<i class="fa fa-undo"></i>'
31337                     }
31338                 ]
31339             },
31340             {
31341                 tag : 'div',
31342                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31343                 action : 'rotate-right',
31344                 cn : [
31345                     {
31346                         tag : 'button',
31347                         cls : 'btn btn-default',
31348                         html : '<i class="fa fa-repeat"></i>'
31349                     }
31350                 ]
31351             }
31352         ]
31353     }
31354 });
31355
31356 /*
31357 * Licence: LGPL
31358 */
31359
31360 /**
31361  * @class Roo.bootstrap.DocumentManager
31362  * @extends Roo.bootstrap.Component
31363  * Bootstrap DocumentManager class
31364  * @cfg {String} paramName default 'imageUpload'
31365  * @cfg {String} toolTipName default 'filename'
31366  * @cfg {String} method default POST
31367  * @cfg {String} url action url
31368  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31369  * @cfg {Boolean} multiple multiple upload default true
31370  * @cfg {Number} thumbSize default 300
31371  * @cfg {String} fieldLabel
31372  * @cfg {Number} labelWidth default 4
31373  * @cfg {String} labelAlign (left|top) default left
31374  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31375 * @cfg {Number} labellg set the width of label (1-12)
31376  * @cfg {Number} labelmd set the width of label (1-12)
31377  * @cfg {Number} labelsm set the width of label (1-12)
31378  * @cfg {Number} labelxs set the width of label (1-12)
31379  * 
31380  * @constructor
31381  * Create a new DocumentManager
31382  * @param {Object} config The config object
31383  */
31384
31385 Roo.bootstrap.DocumentManager = function(config){
31386     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31387     
31388     this.files = [];
31389     this.delegates = [];
31390     
31391     this.addEvents({
31392         /**
31393          * @event initial
31394          * Fire when initial the DocumentManager
31395          * @param {Roo.bootstrap.DocumentManager} this
31396          */
31397         "initial" : true,
31398         /**
31399          * @event inspect
31400          * inspect selected file
31401          * @param {Roo.bootstrap.DocumentManager} this
31402          * @param {File} file
31403          */
31404         "inspect" : true,
31405         /**
31406          * @event exception
31407          * Fire when xhr load exception
31408          * @param {Roo.bootstrap.DocumentManager} this
31409          * @param {XMLHttpRequest} xhr
31410          */
31411         "exception" : true,
31412         /**
31413          * @event afterupload
31414          * Fire when xhr load exception
31415          * @param {Roo.bootstrap.DocumentManager} this
31416          * @param {XMLHttpRequest} xhr
31417          */
31418         "afterupload" : true,
31419         /**
31420          * @event prepare
31421          * prepare the form data
31422          * @param {Roo.bootstrap.DocumentManager} this
31423          * @param {Object} formData
31424          */
31425         "prepare" : true,
31426         /**
31427          * @event remove
31428          * Fire when remove the file
31429          * @param {Roo.bootstrap.DocumentManager} this
31430          * @param {Object} file
31431          */
31432         "remove" : true,
31433         /**
31434          * @event refresh
31435          * Fire after refresh the file
31436          * @param {Roo.bootstrap.DocumentManager} this
31437          */
31438         "refresh" : true,
31439         /**
31440          * @event click
31441          * Fire after click the image
31442          * @param {Roo.bootstrap.DocumentManager} this
31443          * @param {Object} file
31444          */
31445         "click" : true,
31446         /**
31447          * @event edit
31448          * Fire when upload a image and editable set to true
31449          * @param {Roo.bootstrap.DocumentManager} this
31450          * @param {Object} file
31451          */
31452         "edit" : true,
31453         /**
31454          * @event beforeselectfile
31455          * Fire before select file
31456          * @param {Roo.bootstrap.DocumentManager} this
31457          */
31458         "beforeselectfile" : true,
31459         /**
31460          * @event process
31461          * Fire before process file
31462          * @param {Roo.bootstrap.DocumentManager} this
31463          * @param {Object} file
31464          */
31465         "process" : true,
31466         /**
31467          * @event previewrendered
31468          * Fire when preview rendered
31469          * @param {Roo.bootstrap.DocumentManager} this
31470          * @param {Object} file
31471          */
31472         "previewrendered" : true,
31473         /**
31474          */
31475         "previewResize" : true
31476         
31477     });
31478 };
31479
31480 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31481     
31482     boxes : 0,
31483     inputName : '',
31484     thumbSize : 300,
31485     multiple : true,
31486     files : false,
31487     method : 'POST',
31488     url : '',
31489     paramName : 'imageUpload',
31490     toolTipName : 'filename',
31491     fieldLabel : '',
31492     labelWidth : 4,
31493     labelAlign : 'left',
31494     editable : true,
31495     delegates : false,
31496     xhr : false, 
31497     
31498     labellg : 0,
31499     labelmd : 0,
31500     labelsm : 0,
31501     labelxs : 0,
31502     
31503     getAutoCreate : function()
31504     {   
31505         var managerWidget = {
31506             tag : 'div',
31507             cls : 'roo-document-manager',
31508             cn : [
31509                 {
31510                     tag : 'input',
31511                     cls : 'roo-document-manager-selector',
31512                     type : 'file'
31513                 },
31514                 {
31515                     tag : 'div',
31516                     cls : 'roo-document-manager-uploader',
31517                     cn : [
31518                         {
31519                             tag : 'div',
31520                             cls : 'roo-document-manager-upload-btn',
31521                             html : '<i class="fa fa-plus"></i>'
31522                         }
31523                     ]
31524                     
31525                 }
31526             ]
31527         };
31528         
31529         var content = [
31530             {
31531                 tag : 'div',
31532                 cls : 'column col-md-12',
31533                 cn : managerWidget
31534             }
31535         ];
31536         
31537         if(this.fieldLabel.length){
31538             
31539             content = [
31540                 {
31541                     tag : 'div',
31542                     cls : 'column col-md-12',
31543                     html : this.fieldLabel
31544                 },
31545                 {
31546                     tag : 'div',
31547                     cls : 'column col-md-12',
31548                     cn : managerWidget
31549                 }
31550             ];
31551
31552             if(this.labelAlign == 'left'){
31553                 content = [
31554                     {
31555                         tag : 'div',
31556                         cls : 'column',
31557                         html : this.fieldLabel
31558                     },
31559                     {
31560                         tag : 'div',
31561                         cls : 'column',
31562                         cn : managerWidget
31563                     }
31564                 ];
31565                 
31566                 if(this.labelWidth > 12){
31567                     content[0].style = "width: " + this.labelWidth + 'px';
31568                 }
31569
31570                 if(this.labelWidth < 13 && this.labelmd == 0){
31571                     this.labelmd = this.labelWidth;
31572                 }
31573
31574                 if(this.labellg > 0){
31575                     content[0].cls += ' col-lg-' + this.labellg;
31576                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31577                 }
31578
31579                 if(this.labelmd > 0){
31580                     content[0].cls += ' col-md-' + this.labelmd;
31581                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31582                 }
31583
31584                 if(this.labelsm > 0){
31585                     content[0].cls += ' col-sm-' + this.labelsm;
31586                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31587                 }
31588
31589                 if(this.labelxs > 0){
31590                     content[0].cls += ' col-xs-' + this.labelxs;
31591                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31592                 }
31593                 
31594             }
31595         }
31596         
31597         var cfg = {
31598             tag : 'div',
31599             cls : 'row clearfix',
31600             cn : content
31601         };
31602         
31603         return cfg;
31604         
31605     },
31606     
31607     initEvents : function()
31608     {
31609         this.managerEl = this.el.select('.roo-document-manager', true).first();
31610         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31611         
31612         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31613         this.selectorEl.hide();
31614         
31615         if(this.multiple){
31616             this.selectorEl.attr('multiple', 'multiple');
31617         }
31618         
31619         this.selectorEl.on('change', this.onFileSelected, this);
31620         
31621         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31622         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31623         
31624         this.uploader.on('click', this.onUploaderClick, this);
31625         
31626         this.renderProgressDialog();
31627         
31628         var _this = this;
31629         
31630         window.addEventListener("resize", function() { _this.refresh(); } );
31631         
31632         this.fireEvent('initial', this);
31633     },
31634     
31635     renderProgressDialog : function()
31636     {
31637         var _this = this;
31638         
31639         this.progressDialog = new Roo.bootstrap.Modal({
31640             cls : 'roo-document-manager-progress-dialog',
31641             allow_close : false,
31642             animate : false,
31643             title : '',
31644             buttons : [
31645                 {
31646                     name  :'cancel',
31647                     weight : 'danger',
31648                     html : 'Cancel'
31649                 }
31650             ], 
31651             listeners : { 
31652                 btnclick : function() {
31653                     _this.uploadCancel();
31654                     this.hide();
31655                 }
31656             }
31657         });
31658          
31659         this.progressDialog.render(Roo.get(document.body));
31660          
31661         this.progress = new Roo.bootstrap.Progress({
31662             cls : 'roo-document-manager-progress',
31663             active : true,
31664             striped : true
31665         });
31666         
31667         this.progress.render(this.progressDialog.getChildContainer());
31668         
31669         this.progressBar = new Roo.bootstrap.ProgressBar({
31670             cls : 'roo-document-manager-progress-bar',
31671             aria_valuenow : 0,
31672             aria_valuemin : 0,
31673             aria_valuemax : 12,
31674             panel : 'success'
31675         });
31676         
31677         this.progressBar.render(this.progress.getChildContainer());
31678     },
31679     
31680     onUploaderClick : function(e)
31681     {
31682         e.preventDefault();
31683      
31684         if(this.fireEvent('beforeselectfile', this) != false){
31685             this.selectorEl.dom.click();
31686         }
31687         
31688     },
31689     
31690     onFileSelected : function(e)
31691     {
31692         e.preventDefault();
31693         
31694         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31695             return;
31696         }
31697         
31698         Roo.each(this.selectorEl.dom.files, function(file){
31699             if(this.fireEvent('inspect', this, file) != false){
31700                 this.files.push(file);
31701             }
31702         }, this);
31703         
31704         this.queue();
31705         
31706     },
31707     
31708     queue : function()
31709     {
31710         this.selectorEl.dom.value = '';
31711         
31712         if(!this.files || !this.files.length){
31713             return;
31714         }
31715         
31716         if(this.boxes > 0 && this.files.length > this.boxes){
31717             this.files = this.files.slice(0, this.boxes);
31718         }
31719         
31720         this.uploader.show();
31721         
31722         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31723             this.uploader.hide();
31724         }
31725         
31726         var _this = this;
31727         
31728         var files = [];
31729         
31730         var docs = [];
31731         
31732         Roo.each(this.files, function(file){
31733             
31734             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31735                 var f = this.renderPreview(file);
31736                 files.push(f);
31737                 return;
31738             }
31739             
31740             if(file.type.indexOf('image') != -1){
31741                 this.delegates.push(
31742                     (function(){
31743                         _this.process(file);
31744                     }).createDelegate(this)
31745                 );
31746         
31747                 return;
31748             }
31749             
31750             docs.push(
31751                 (function(){
31752                     _this.process(file);
31753                 }).createDelegate(this)
31754             );
31755             
31756         }, this);
31757         
31758         this.files = files;
31759         
31760         this.delegates = this.delegates.concat(docs);
31761         
31762         if(!this.delegates.length){
31763             this.refresh();
31764             return;
31765         }
31766         
31767         this.progressBar.aria_valuemax = this.delegates.length;
31768         
31769         this.arrange();
31770         
31771         return;
31772     },
31773     
31774     arrange : function()
31775     {
31776         if(!this.delegates.length){
31777             this.progressDialog.hide();
31778             this.refresh();
31779             return;
31780         }
31781         
31782         var delegate = this.delegates.shift();
31783         
31784         this.progressDialog.show();
31785         
31786         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31787         
31788         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31789         
31790         delegate();
31791     },
31792     
31793     refresh : function()
31794     {
31795         this.uploader.show();
31796         
31797         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31798             this.uploader.hide();
31799         }
31800         
31801         Roo.isTouch ? this.closable(false) : this.closable(true);
31802         
31803         this.fireEvent('refresh', this);
31804     },
31805     
31806     onRemove : function(e, el, o)
31807     {
31808         e.preventDefault();
31809         
31810         this.fireEvent('remove', this, o);
31811         
31812     },
31813     
31814     remove : function(o)
31815     {
31816         var files = [];
31817         
31818         Roo.each(this.files, function(file){
31819             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31820                 files.push(file);
31821                 return;
31822             }
31823
31824             o.target.remove();
31825
31826         }, this);
31827         
31828         this.files = files;
31829         
31830         this.refresh();
31831     },
31832     
31833     clear : function()
31834     {
31835         Roo.each(this.files, function(file){
31836             if(!file.target){
31837                 return;
31838             }
31839             
31840             file.target.remove();
31841
31842         }, this);
31843         
31844         this.files = [];
31845         
31846         this.refresh();
31847     },
31848     
31849     onClick : function(e, el, o)
31850     {
31851         e.preventDefault();
31852         
31853         this.fireEvent('click', this, o);
31854         
31855     },
31856     
31857     closable : function(closable)
31858     {
31859         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31860             
31861             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31862             
31863             if(closable){
31864                 el.show();
31865                 return;
31866             }
31867             
31868             el.hide();
31869             
31870         }, this);
31871     },
31872     
31873     xhrOnLoad : function(xhr)
31874     {
31875         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31876             el.remove();
31877         }, this);
31878         
31879         if (xhr.readyState !== 4) {
31880             this.arrange();
31881             this.fireEvent('exception', this, xhr);
31882             return;
31883         }
31884
31885         var response = Roo.decode(xhr.responseText);
31886         
31887         if(!response.success){
31888             this.arrange();
31889             this.fireEvent('exception', this, xhr);
31890             return;
31891         }
31892         
31893         var file = this.renderPreview(response.data);
31894         
31895         this.files.push(file);
31896         
31897         this.arrange();
31898         
31899         this.fireEvent('afterupload', this, xhr);
31900         
31901     },
31902     
31903     xhrOnError : function(xhr)
31904     {
31905         Roo.log('xhr on error');
31906         
31907         var response = Roo.decode(xhr.responseText);
31908           
31909         Roo.log(response);
31910         
31911         this.arrange();
31912     },
31913     
31914     process : function(file)
31915     {
31916         if(this.fireEvent('process', this, file) !== false){
31917             if(this.editable && file.type.indexOf('image') != -1){
31918                 this.fireEvent('edit', this, file);
31919                 return;
31920             }
31921
31922             this.uploadStart(file, false);
31923
31924             return;
31925         }
31926         
31927     },
31928     
31929     uploadStart : function(file, crop)
31930     {
31931         this.xhr = new XMLHttpRequest();
31932         
31933         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31934             this.arrange();
31935             return;
31936         }
31937         
31938         file.xhr = this.xhr;
31939             
31940         this.managerEl.createChild({
31941             tag : 'div',
31942             cls : 'roo-document-manager-loading',
31943             cn : [
31944                 {
31945                     tag : 'div',
31946                     tooltip : file.name,
31947                     cls : 'roo-document-manager-thumb',
31948                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31949                 }
31950             ]
31951
31952         });
31953
31954         this.xhr.open(this.method, this.url, true);
31955         
31956         var headers = {
31957             "Accept": "application/json",
31958             "Cache-Control": "no-cache",
31959             "X-Requested-With": "XMLHttpRequest"
31960         };
31961         
31962         for (var headerName in headers) {
31963             var headerValue = headers[headerName];
31964             if (headerValue) {
31965                 this.xhr.setRequestHeader(headerName, headerValue);
31966             }
31967         }
31968         
31969         var _this = this;
31970         
31971         this.xhr.onload = function()
31972         {
31973             _this.xhrOnLoad(_this.xhr);
31974         }
31975         
31976         this.xhr.onerror = function()
31977         {
31978             _this.xhrOnError(_this.xhr);
31979         }
31980         
31981         var formData = new FormData();
31982
31983         formData.append('returnHTML', 'NO');
31984         
31985         if(crop){
31986             formData.append('crop', crop);
31987         }
31988         
31989         formData.append(this.paramName, file, file.name);
31990         
31991         var options = {
31992             file : file, 
31993             manually : false
31994         };
31995         
31996         if(this.fireEvent('prepare', this, formData, options) != false){
31997             
31998             if(options.manually){
31999                 return;
32000             }
32001             
32002             this.xhr.send(formData);
32003             return;
32004         };
32005         
32006         this.uploadCancel();
32007     },
32008     
32009     uploadCancel : function()
32010     {
32011         if (this.xhr) {
32012             this.xhr.abort();
32013         }
32014         
32015         this.delegates = [];
32016         
32017         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32018             el.remove();
32019         }, this);
32020         
32021         this.arrange();
32022     },
32023     
32024     renderPreview : function(file)
32025     {
32026         if(typeof(file.target) != 'undefined' && file.target){
32027             return file;
32028         }
32029         
32030         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32031         
32032         var previewEl = this.managerEl.createChild({
32033             tag : 'div',
32034             cls : 'roo-document-manager-preview',
32035             cn : [
32036                 {
32037                     tag : 'div',
32038                     tooltip : file[this.toolTipName],
32039                     cls : 'roo-document-manager-thumb',
32040                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32041                 },
32042                 {
32043                     tag : 'button',
32044                     cls : 'close',
32045                     html : '<i class="fa fa-times-circle"></i>'
32046                 }
32047             ]
32048         });
32049
32050         var close = previewEl.select('button.close', true).first();
32051
32052         close.on('click', this.onRemove, this, file);
32053
32054         file.target = previewEl;
32055
32056         var image = previewEl.select('img', true).first();
32057         
32058         var _this = this;
32059         
32060         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32061         
32062         image.on('click', this.onClick, this, file);
32063         
32064         this.fireEvent('previewrendered', this, file);
32065         
32066         return file;
32067         
32068     },
32069     
32070     onPreviewLoad : function(file, image)
32071     {
32072         if(typeof(file.target) == 'undefined' || !file.target){
32073             return;
32074         }
32075         
32076         var width = image.dom.naturalWidth || image.dom.width;
32077         var height = image.dom.naturalHeight || image.dom.height;
32078         
32079         if(!this.previewResize) {
32080             return;
32081         }
32082         
32083         if(width > height){
32084             file.target.addClass('wide');
32085             return;
32086         }
32087         
32088         file.target.addClass('tall');
32089         return;
32090         
32091     },
32092     
32093     uploadFromSource : function(file, crop)
32094     {
32095         this.xhr = new XMLHttpRequest();
32096         
32097         this.managerEl.createChild({
32098             tag : 'div',
32099             cls : 'roo-document-manager-loading',
32100             cn : [
32101                 {
32102                     tag : 'div',
32103                     tooltip : file.name,
32104                     cls : 'roo-document-manager-thumb',
32105                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32106                 }
32107             ]
32108
32109         });
32110
32111         this.xhr.open(this.method, this.url, true);
32112         
32113         var headers = {
32114             "Accept": "application/json",
32115             "Cache-Control": "no-cache",
32116             "X-Requested-With": "XMLHttpRequest"
32117         };
32118         
32119         for (var headerName in headers) {
32120             var headerValue = headers[headerName];
32121             if (headerValue) {
32122                 this.xhr.setRequestHeader(headerName, headerValue);
32123             }
32124         }
32125         
32126         var _this = this;
32127         
32128         this.xhr.onload = function()
32129         {
32130             _this.xhrOnLoad(_this.xhr);
32131         }
32132         
32133         this.xhr.onerror = function()
32134         {
32135             _this.xhrOnError(_this.xhr);
32136         }
32137         
32138         var formData = new FormData();
32139
32140         formData.append('returnHTML', 'NO');
32141         
32142         formData.append('crop', crop);
32143         
32144         if(typeof(file.filename) != 'undefined'){
32145             formData.append('filename', file.filename);
32146         }
32147         
32148         if(typeof(file.mimetype) != 'undefined'){
32149             formData.append('mimetype', file.mimetype);
32150         }
32151         
32152         Roo.log(formData);
32153         
32154         if(this.fireEvent('prepare', this, formData) != false){
32155             this.xhr.send(formData);
32156         };
32157     }
32158 });
32159
32160 /*
32161 * Licence: LGPL
32162 */
32163
32164 /**
32165  * @class Roo.bootstrap.DocumentViewer
32166  * @extends Roo.bootstrap.Component
32167  * Bootstrap DocumentViewer class
32168  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32169  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32170  * 
32171  * @constructor
32172  * Create a new DocumentViewer
32173  * @param {Object} config The config object
32174  */
32175
32176 Roo.bootstrap.DocumentViewer = function(config){
32177     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32178     
32179     this.addEvents({
32180         /**
32181          * @event initial
32182          * Fire after initEvent
32183          * @param {Roo.bootstrap.DocumentViewer} this
32184          */
32185         "initial" : true,
32186         /**
32187          * @event click
32188          * Fire after click
32189          * @param {Roo.bootstrap.DocumentViewer} this
32190          */
32191         "click" : true,
32192         /**
32193          * @event download
32194          * Fire after download button
32195          * @param {Roo.bootstrap.DocumentViewer} this
32196          */
32197         "download" : true,
32198         /**
32199          * @event trash
32200          * Fire after trash button
32201          * @param {Roo.bootstrap.DocumentViewer} this
32202          */
32203         "trash" : true
32204         
32205     });
32206 };
32207
32208 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32209     
32210     showDownload : true,
32211     
32212     showTrash : true,
32213     
32214     getAutoCreate : function()
32215     {
32216         var cfg = {
32217             tag : 'div',
32218             cls : 'roo-document-viewer',
32219             cn : [
32220                 {
32221                     tag : 'div',
32222                     cls : 'roo-document-viewer-body',
32223                     cn : [
32224                         {
32225                             tag : 'div',
32226                             cls : 'roo-document-viewer-thumb',
32227                             cn : [
32228                                 {
32229                                     tag : 'img',
32230                                     cls : 'roo-document-viewer-image'
32231                                 }
32232                             ]
32233                         }
32234                     ]
32235                 },
32236                 {
32237                     tag : 'div',
32238                     cls : 'roo-document-viewer-footer',
32239                     cn : {
32240                         tag : 'div',
32241                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32242                         cn : [
32243                             {
32244                                 tag : 'div',
32245                                 cls : 'btn-group roo-document-viewer-download',
32246                                 cn : [
32247                                     {
32248                                         tag : 'button',
32249                                         cls : 'btn btn-default',
32250                                         html : '<i class="fa fa-download"></i>'
32251                                     }
32252                                 ]
32253                             },
32254                             {
32255                                 tag : 'div',
32256                                 cls : 'btn-group roo-document-viewer-trash',
32257                                 cn : [
32258                                     {
32259                                         tag : 'button',
32260                                         cls : 'btn btn-default',
32261                                         html : '<i class="fa fa-trash"></i>'
32262                                     }
32263                                 ]
32264                             }
32265                         ]
32266                     }
32267                 }
32268             ]
32269         };
32270         
32271         return cfg;
32272     },
32273     
32274     initEvents : function()
32275     {
32276         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32277         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32278         
32279         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32280         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32281         
32282         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32283         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32284         
32285         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32286         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32287         
32288         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32289         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32290         
32291         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32292         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32293         
32294         this.bodyEl.on('click', this.onClick, this);
32295         this.downloadBtn.on('click', this.onDownload, this);
32296         this.trashBtn.on('click', this.onTrash, this);
32297         
32298         this.downloadBtn.hide();
32299         this.trashBtn.hide();
32300         
32301         if(this.showDownload){
32302             this.downloadBtn.show();
32303         }
32304         
32305         if(this.showTrash){
32306             this.trashBtn.show();
32307         }
32308         
32309         if(!this.showDownload && !this.showTrash) {
32310             this.footerEl.hide();
32311         }
32312         
32313     },
32314     
32315     initial : function()
32316     {
32317         this.fireEvent('initial', this);
32318         
32319     },
32320     
32321     onClick : function(e)
32322     {
32323         e.preventDefault();
32324         
32325         this.fireEvent('click', this);
32326     },
32327     
32328     onDownload : function(e)
32329     {
32330         e.preventDefault();
32331         
32332         this.fireEvent('download', this);
32333     },
32334     
32335     onTrash : function(e)
32336     {
32337         e.preventDefault();
32338         
32339         this.fireEvent('trash', this);
32340     }
32341     
32342 });
32343 /*
32344  * - LGPL
32345  *
32346  * nav progress bar
32347  * 
32348  */
32349
32350 /**
32351  * @class Roo.bootstrap.NavProgressBar
32352  * @extends Roo.bootstrap.Component
32353  * Bootstrap NavProgressBar class
32354  * 
32355  * @constructor
32356  * Create a new nav progress bar
32357  * @param {Object} config The config object
32358  */
32359
32360 Roo.bootstrap.NavProgressBar = function(config){
32361     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32362
32363     this.bullets = this.bullets || [];
32364    
32365 //    Roo.bootstrap.NavProgressBar.register(this);
32366      this.addEvents({
32367         /**
32368              * @event changed
32369              * Fires when the active item changes
32370              * @param {Roo.bootstrap.NavProgressBar} this
32371              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32372              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32373          */
32374         'changed': true
32375      });
32376     
32377 };
32378
32379 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32380     
32381     bullets : [],
32382     barItems : [],
32383     
32384     getAutoCreate : function()
32385     {
32386         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32387         
32388         cfg = {
32389             tag : 'div',
32390             cls : 'roo-navigation-bar-group',
32391             cn : [
32392                 {
32393                     tag : 'div',
32394                     cls : 'roo-navigation-top-bar'
32395                 },
32396                 {
32397                     tag : 'div',
32398                     cls : 'roo-navigation-bullets-bar',
32399                     cn : [
32400                         {
32401                             tag : 'ul',
32402                             cls : 'roo-navigation-bar'
32403                         }
32404                     ]
32405                 },
32406                 
32407                 {
32408                     tag : 'div',
32409                     cls : 'roo-navigation-bottom-bar'
32410                 }
32411             ]
32412             
32413         };
32414         
32415         return cfg;
32416         
32417     },
32418     
32419     initEvents: function() 
32420     {
32421         
32422     },
32423     
32424     onRender : function(ct, position) 
32425     {
32426         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32427         
32428         if(this.bullets.length){
32429             Roo.each(this.bullets, function(b){
32430                this.addItem(b);
32431             }, this);
32432         }
32433         
32434         this.format();
32435         
32436     },
32437     
32438     addItem : function(cfg)
32439     {
32440         var item = new Roo.bootstrap.NavProgressItem(cfg);
32441         
32442         item.parentId = this.id;
32443         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32444         
32445         if(cfg.html){
32446             var top = new Roo.bootstrap.Element({
32447                 tag : 'div',
32448                 cls : 'roo-navigation-bar-text'
32449             });
32450             
32451             var bottom = new Roo.bootstrap.Element({
32452                 tag : 'div',
32453                 cls : 'roo-navigation-bar-text'
32454             });
32455             
32456             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32457             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32458             
32459             var topText = new Roo.bootstrap.Element({
32460                 tag : 'span',
32461                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32462             });
32463             
32464             var bottomText = new Roo.bootstrap.Element({
32465                 tag : 'span',
32466                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32467             });
32468             
32469             topText.onRender(top.el, null);
32470             bottomText.onRender(bottom.el, null);
32471             
32472             item.topEl = top;
32473             item.bottomEl = bottom;
32474         }
32475         
32476         this.barItems.push(item);
32477         
32478         return item;
32479     },
32480     
32481     getActive : function()
32482     {
32483         var active = false;
32484         
32485         Roo.each(this.barItems, function(v){
32486             
32487             if (!v.isActive()) {
32488                 return;
32489             }
32490             
32491             active = v;
32492             return false;
32493             
32494         });
32495         
32496         return active;
32497     },
32498     
32499     setActiveItem : function(item)
32500     {
32501         var prev = false;
32502         
32503         Roo.each(this.barItems, function(v){
32504             if (v.rid == item.rid) {
32505                 return ;
32506             }
32507             
32508             if (v.isActive()) {
32509                 v.setActive(false);
32510                 prev = v;
32511             }
32512         });
32513
32514         item.setActive(true);
32515         
32516         this.fireEvent('changed', this, item, prev);
32517     },
32518     
32519     getBarItem: function(rid)
32520     {
32521         var ret = false;
32522         
32523         Roo.each(this.barItems, function(e) {
32524             if (e.rid != rid) {
32525                 return;
32526             }
32527             
32528             ret =  e;
32529             return false;
32530         });
32531         
32532         return ret;
32533     },
32534     
32535     indexOfItem : function(item)
32536     {
32537         var index = false;
32538         
32539         Roo.each(this.barItems, function(v, i){
32540             
32541             if (v.rid != item.rid) {
32542                 return;
32543             }
32544             
32545             index = i;
32546             return false
32547         });
32548         
32549         return index;
32550     },
32551     
32552     setActiveNext : function()
32553     {
32554         var i = this.indexOfItem(this.getActive());
32555         
32556         if (i > this.barItems.length) {
32557             return;
32558         }
32559         
32560         this.setActiveItem(this.barItems[i+1]);
32561     },
32562     
32563     setActivePrev : function()
32564     {
32565         var i = this.indexOfItem(this.getActive());
32566         
32567         if (i  < 1) {
32568             return;
32569         }
32570         
32571         this.setActiveItem(this.barItems[i-1]);
32572     },
32573     
32574     format : function()
32575     {
32576         if(!this.barItems.length){
32577             return;
32578         }
32579      
32580         var width = 100 / this.barItems.length;
32581         
32582         Roo.each(this.barItems, function(i){
32583             i.el.setStyle('width', width + '%');
32584             i.topEl.el.setStyle('width', width + '%');
32585             i.bottomEl.el.setStyle('width', width + '%');
32586         }, this);
32587         
32588     }
32589     
32590 });
32591 /*
32592  * - LGPL
32593  *
32594  * Nav Progress Item
32595  * 
32596  */
32597
32598 /**
32599  * @class Roo.bootstrap.NavProgressItem
32600  * @extends Roo.bootstrap.Component
32601  * Bootstrap NavProgressItem class
32602  * @cfg {String} rid the reference id
32603  * @cfg {Boolean} active (true|false) Is item active default false
32604  * @cfg {Boolean} disabled (true|false) Is item active default false
32605  * @cfg {String} html
32606  * @cfg {String} position (top|bottom) text position default bottom
32607  * @cfg {String} icon show icon instead of number
32608  * 
32609  * @constructor
32610  * Create a new NavProgressItem
32611  * @param {Object} config The config object
32612  */
32613 Roo.bootstrap.NavProgressItem = function(config){
32614     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32615     this.addEvents({
32616         // raw events
32617         /**
32618          * @event click
32619          * The raw click event for the entire grid.
32620          * @param {Roo.bootstrap.NavProgressItem} this
32621          * @param {Roo.EventObject} e
32622          */
32623         "click" : true
32624     });
32625    
32626 };
32627
32628 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32629     
32630     rid : '',
32631     active : false,
32632     disabled : false,
32633     html : '',
32634     position : 'bottom',
32635     icon : false,
32636     
32637     getAutoCreate : function()
32638     {
32639         var iconCls = 'roo-navigation-bar-item-icon';
32640         
32641         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32642         
32643         var cfg = {
32644             tag: 'li',
32645             cls: 'roo-navigation-bar-item',
32646             cn : [
32647                 {
32648                     tag : 'i',
32649                     cls : iconCls
32650                 }
32651             ]
32652         };
32653         
32654         if(this.active){
32655             cfg.cls += ' active';
32656         }
32657         if(this.disabled){
32658             cfg.cls += ' disabled';
32659         }
32660         
32661         return cfg;
32662     },
32663     
32664     disable : function()
32665     {
32666         this.setDisabled(true);
32667     },
32668     
32669     enable : function()
32670     {
32671         this.setDisabled(false);
32672     },
32673     
32674     initEvents: function() 
32675     {
32676         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32677         
32678         this.iconEl.on('click', this.onClick, this);
32679     },
32680     
32681     onClick : function(e)
32682     {
32683         e.preventDefault();
32684         
32685         if(this.disabled){
32686             return;
32687         }
32688         
32689         if(this.fireEvent('click', this, e) === false){
32690             return;
32691         };
32692         
32693         this.parent().setActiveItem(this);
32694     },
32695     
32696     isActive: function () 
32697     {
32698         return this.active;
32699     },
32700     
32701     setActive : function(state)
32702     {
32703         if(this.active == state){
32704             return;
32705         }
32706         
32707         this.active = state;
32708         
32709         if (state) {
32710             this.el.addClass('active');
32711             return;
32712         }
32713         
32714         this.el.removeClass('active');
32715         
32716         return;
32717     },
32718     
32719     setDisabled : function(state)
32720     {
32721         if(this.disabled == state){
32722             return;
32723         }
32724         
32725         this.disabled = state;
32726         
32727         if (state) {
32728             this.el.addClass('disabled');
32729             return;
32730         }
32731         
32732         this.el.removeClass('disabled');
32733     },
32734     
32735     tooltipEl : function()
32736     {
32737         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32738     }
32739 });
32740  
32741
32742  /*
32743  * - LGPL
32744  *
32745  * FieldLabel
32746  * 
32747  */
32748
32749 /**
32750  * @class Roo.bootstrap.FieldLabel
32751  * @extends Roo.bootstrap.Component
32752  * Bootstrap FieldLabel class
32753  * @cfg {String} html contents of the element
32754  * @cfg {String} tag tag of the element default label
32755  * @cfg {String} cls class of the element
32756  * @cfg {String} target label target 
32757  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32758  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32759  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32760  * @cfg {String} iconTooltip default "This field is required"
32761  * @cfg {String} indicatorpos (left|right) default left
32762  * 
32763  * @constructor
32764  * Create a new FieldLabel
32765  * @param {Object} config The config object
32766  */
32767
32768 Roo.bootstrap.FieldLabel = function(config){
32769     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32770     
32771     this.addEvents({
32772             /**
32773              * @event invalid
32774              * Fires after the field has been marked as invalid.
32775              * @param {Roo.form.FieldLabel} this
32776              * @param {String} msg The validation message
32777              */
32778             invalid : true,
32779             /**
32780              * @event valid
32781              * Fires after the field has been validated with no errors.
32782              * @param {Roo.form.FieldLabel} this
32783              */
32784             valid : true
32785         });
32786 };
32787
32788 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32789     
32790     tag: 'label',
32791     cls: '',
32792     html: '',
32793     target: '',
32794     allowBlank : true,
32795     invalidClass : 'has-warning',
32796     validClass : 'has-success',
32797     iconTooltip : 'This field is required',
32798     indicatorpos : 'left',
32799     
32800     getAutoCreate : function(){
32801         
32802         var cls = "";
32803         if (!this.allowBlank) {
32804             cls  = "visible";
32805         }
32806         
32807         var cfg = {
32808             tag : this.tag,
32809             cls : 'roo-bootstrap-field-label ' + this.cls,
32810             for : this.target,
32811             cn : [
32812                 {
32813                     tag : 'i',
32814                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32815                     tooltip : this.iconTooltip
32816                 },
32817                 {
32818                     tag : 'span',
32819                     html : this.html
32820                 }
32821             ] 
32822         };
32823         
32824         if(this.indicatorpos == 'right'){
32825             var cfg = {
32826                 tag : this.tag,
32827                 cls : 'roo-bootstrap-field-label ' + this.cls,
32828                 for : this.target,
32829                 cn : [
32830                     {
32831                         tag : 'span',
32832                         html : this.html
32833                     },
32834                     {
32835                         tag : 'i',
32836                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32837                         tooltip : this.iconTooltip
32838                     }
32839                 ] 
32840             };
32841         }
32842         
32843         return cfg;
32844     },
32845     
32846     initEvents: function() 
32847     {
32848         Roo.bootstrap.Element.superclass.initEvents.call(this);
32849         
32850         this.indicator = this.indicatorEl();
32851         
32852         if(this.indicator){
32853             this.indicator.removeClass('visible');
32854             this.indicator.addClass('invisible');
32855         }
32856         
32857         Roo.bootstrap.FieldLabel.register(this);
32858     },
32859     
32860     indicatorEl : function()
32861     {
32862         var indicator = this.el.select('i.roo-required-indicator',true).first();
32863         
32864         if(!indicator){
32865             return false;
32866         }
32867         
32868         return indicator;
32869         
32870     },
32871     
32872     /**
32873      * Mark this field as valid
32874      */
32875     markValid : function()
32876     {
32877         if(this.indicator){
32878             this.indicator.removeClass('visible');
32879             this.indicator.addClass('invisible');
32880         }
32881         if (Roo.bootstrap.version == 3) {
32882             this.el.removeClass(this.invalidClass);
32883             this.el.addClass(this.validClass);
32884         } else {
32885             this.el.removeClass('is-invalid');
32886             this.el.addClass('is-valid');
32887         }
32888         
32889         
32890         this.fireEvent('valid', this);
32891     },
32892     
32893     /**
32894      * Mark this field as invalid
32895      * @param {String} msg The validation message
32896      */
32897     markInvalid : function(msg)
32898     {
32899         if(this.indicator){
32900             this.indicator.removeClass('invisible');
32901             this.indicator.addClass('visible');
32902         }
32903           if (Roo.bootstrap.version == 3) {
32904             this.el.removeClass(this.validClass);
32905             this.el.addClass(this.invalidClass);
32906         } else {
32907             this.el.removeClass('is-valid');
32908             this.el.addClass('is-invalid');
32909         }
32910         
32911         
32912         this.fireEvent('invalid', this, msg);
32913     }
32914     
32915    
32916 });
32917
32918 Roo.apply(Roo.bootstrap.FieldLabel, {
32919     
32920     groups: {},
32921     
32922      /**
32923     * register a FieldLabel Group
32924     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32925     */
32926     register : function(label)
32927     {
32928         if(this.groups.hasOwnProperty(label.target)){
32929             return;
32930         }
32931      
32932         this.groups[label.target] = label;
32933         
32934     },
32935     /**
32936     * fetch a FieldLabel Group based on the target
32937     * @param {string} target
32938     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32939     */
32940     get: function(target) {
32941         if (typeof(this.groups[target]) == 'undefined') {
32942             return false;
32943         }
32944         
32945         return this.groups[target] ;
32946     }
32947 });
32948
32949  
32950
32951  /*
32952  * - LGPL
32953  *
32954  * page DateSplitField.
32955  * 
32956  */
32957
32958
32959 /**
32960  * @class Roo.bootstrap.DateSplitField
32961  * @extends Roo.bootstrap.Component
32962  * Bootstrap DateSplitField class
32963  * @cfg {string} fieldLabel - the label associated
32964  * @cfg {Number} labelWidth set the width of label (0-12)
32965  * @cfg {String} labelAlign (top|left)
32966  * @cfg {Boolean} dayAllowBlank (true|false) default false
32967  * @cfg {Boolean} monthAllowBlank (true|false) default false
32968  * @cfg {Boolean} yearAllowBlank (true|false) default false
32969  * @cfg {string} dayPlaceholder 
32970  * @cfg {string} monthPlaceholder
32971  * @cfg {string} yearPlaceholder
32972  * @cfg {string} dayFormat default 'd'
32973  * @cfg {string} monthFormat default 'm'
32974  * @cfg {string} yearFormat default 'Y'
32975  * @cfg {Number} labellg set the width of label (1-12)
32976  * @cfg {Number} labelmd set the width of label (1-12)
32977  * @cfg {Number} labelsm set the width of label (1-12)
32978  * @cfg {Number} labelxs set the width of label (1-12)
32979
32980  *     
32981  * @constructor
32982  * Create a new DateSplitField
32983  * @param {Object} config The config object
32984  */
32985
32986 Roo.bootstrap.DateSplitField = function(config){
32987     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32988     
32989     this.addEvents({
32990         // raw events
32991          /**
32992          * @event years
32993          * getting the data of years
32994          * @param {Roo.bootstrap.DateSplitField} this
32995          * @param {Object} years
32996          */
32997         "years" : true,
32998         /**
32999          * @event days
33000          * getting the data of days
33001          * @param {Roo.bootstrap.DateSplitField} this
33002          * @param {Object} days
33003          */
33004         "days" : true,
33005         /**
33006          * @event invalid
33007          * Fires after the field has been marked as invalid.
33008          * @param {Roo.form.Field} this
33009          * @param {String} msg The validation message
33010          */
33011         invalid : true,
33012        /**
33013          * @event valid
33014          * Fires after the field has been validated with no errors.
33015          * @param {Roo.form.Field} this
33016          */
33017         valid : true
33018     });
33019 };
33020
33021 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33022     
33023     fieldLabel : '',
33024     labelAlign : 'top',
33025     labelWidth : 3,
33026     dayAllowBlank : false,
33027     monthAllowBlank : false,
33028     yearAllowBlank : false,
33029     dayPlaceholder : '',
33030     monthPlaceholder : '',
33031     yearPlaceholder : '',
33032     dayFormat : 'd',
33033     monthFormat : 'm',
33034     yearFormat : 'Y',
33035     isFormField : true,
33036     labellg : 0,
33037     labelmd : 0,
33038     labelsm : 0,
33039     labelxs : 0,
33040     
33041     getAutoCreate : function()
33042     {
33043         var cfg = {
33044             tag : 'div',
33045             cls : 'row roo-date-split-field-group',
33046             cn : [
33047                 {
33048                     tag : 'input',
33049                     type : 'hidden',
33050                     cls : 'form-hidden-field roo-date-split-field-group-value',
33051                     name : this.name
33052                 }
33053             ]
33054         };
33055         
33056         var labelCls = 'col-md-12';
33057         var contentCls = 'col-md-4';
33058         
33059         if(this.fieldLabel){
33060             
33061             var label = {
33062                 tag : 'div',
33063                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33064                 cn : [
33065                     {
33066                         tag : 'label',
33067                         html : this.fieldLabel
33068                     }
33069                 ]
33070             };
33071             
33072             if(this.labelAlign == 'left'){
33073             
33074                 if(this.labelWidth > 12){
33075                     label.style = "width: " + this.labelWidth + 'px';
33076                 }
33077
33078                 if(this.labelWidth < 13 && this.labelmd == 0){
33079                     this.labelmd = this.labelWidth;
33080                 }
33081
33082                 if(this.labellg > 0){
33083                     labelCls = ' col-lg-' + this.labellg;
33084                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33085                 }
33086
33087                 if(this.labelmd > 0){
33088                     labelCls = ' col-md-' + this.labelmd;
33089                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33090                 }
33091
33092                 if(this.labelsm > 0){
33093                     labelCls = ' col-sm-' + this.labelsm;
33094                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33095                 }
33096
33097                 if(this.labelxs > 0){
33098                     labelCls = ' col-xs-' + this.labelxs;
33099                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33100                 }
33101             }
33102             
33103             label.cls += ' ' + labelCls;
33104             
33105             cfg.cn.push(label);
33106         }
33107         
33108         Roo.each(['day', 'month', 'year'], function(t){
33109             cfg.cn.push({
33110                 tag : 'div',
33111                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33112             });
33113         }, this);
33114         
33115         return cfg;
33116     },
33117     
33118     inputEl: function ()
33119     {
33120         return this.el.select('.roo-date-split-field-group-value', true).first();
33121     },
33122     
33123     onRender : function(ct, position) 
33124     {
33125         var _this = this;
33126         
33127         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33128         
33129         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33130         
33131         this.dayField = new Roo.bootstrap.ComboBox({
33132             allowBlank : this.dayAllowBlank,
33133             alwaysQuery : true,
33134             displayField : 'value',
33135             editable : false,
33136             fieldLabel : '',
33137             forceSelection : true,
33138             mode : 'local',
33139             placeholder : this.dayPlaceholder,
33140             selectOnFocus : true,
33141             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33142             triggerAction : 'all',
33143             typeAhead : true,
33144             valueField : 'value',
33145             store : new Roo.data.SimpleStore({
33146                 data : (function() {    
33147                     var days = [];
33148                     _this.fireEvent('days', _this, days);
33149                     return days;
33150                 })(),
33151                 fields : [ 'value' ]
33152             }),
33153             listeners : {
33154                 select : function (_self, record, index)
33155                 {
33156                     _this.setValue(_this.getValue());
33157                 }
33158             }
33159         });
33160
33161         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33162         
33163         this.monthField = new Roo.bootstrap.MonthField({
33164             after : '<i class=\"fa fa-calendar\"></i>',
33165             allowBlank : this.monthAllowBlank,
33166             placeholder : this.monthPlaceholder,
33167             readOnly : true,
33168             listeners : {
33169                 render : function (_self)
33170                 {
33171                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33172                         e.preventDefault();
33173                         _self.focus();
33174                     });
33175                 },
33176                 select : function (_self, oldvalue, newvalue)
33177                 {
33178                     _this.setValue(_this.getValue());
33179                 }
33180             }
33181         });
33182         
33183         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33184         
33185         this.yearField = new Roo.bootstrap.ComboBox({
33186             allowBlank : this.yearAllowBlank,
33187             alwaysQuery : true,
33188             displayField : 'value',
33189             editable : false,
33190             fieldLabel : '',
33191             forceSelection : true,
33192             mode : 'local',
33193             placeholder : this.yearPlaceholder,
33194             selectOnFocus : true,
33195             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33196             triggerAction : 'all',
33197             typeAhead : true,
33198             valueField : 'value',
33199             store : new Roo.data.SimpleStore({
33200                 data : (function() {
33201                     var years = [];
33202                     _this.fireEvent('years', _this, years);
33203                     return years;
33204                 })(),
33205                 fields : [ 'value' ]
33206             }),
33207             listeners : {
33208                 select : function (_self, record, index)
33209                 {
33210                     _this.setValue(_this.getValue());
33211                 }
33212             }
33213         });
33214
33215         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33216     },
33217     
33218     setValue : function(v, format)
33219     {
33220         this.inputEl.dom.value = v;
33221         
33222         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33223         
33224         var d = Date.parseDate(v, f);
33225         
33226         if(!d){
33227             this.validate();
33228             return;
33229         }
33230         
33231         this.setDay(d.format(this.dayFormat));
33232         this.setMonth(d.format(this.monthFormat));
33233         this.setYear(d.format(this.yearFormat));
33234         
33235         this.validate();
33236         
33237         return;
33238     },
33239     
33240     setDay : function(v)
33241     {
33242         this.dayField.setValue(v);
33243         this.inputEl.dom.value = this.getValue();
33244         this.validate();
33245         return;
33246     },
33247     
33248     setMonth : function(v)
33249     {
33250         this.monthField.setValue(v, true);
33251         this.inputEl.dom.value = this.getValue();
33252         this.validate();
33253         return;
33254     },
33255     
33256     setYear : function(v)
33257     {
33258         this.yearField.setValue(v);
33259         this.inputEl.dom.value = this.getValue();
33260         this.validate();
33261         return;
33262     },
33263     
33264     getDay : function()
33265     {
33266         return this.dayField.getValue();
33267     },
33268     
33269     getMonth : function()
33270     {
33271         return this.monthField.getValue();
33272     },
33273     
33274     getYear : function()
33275     {
33276         return this.yearField.getValue();
33277     },
33278     
33279     getValue : function()
33280     {
33281         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33282         
33283         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33284         
33285         return date;
33286     },
33287     
33288     reset : function()
33289     {
33290         this.setDay('');
33291         this.setMonth('');
33292         this.setYear('');
33293         this.inputEl.dom.value = '';
33294         this.validate();
33295         return;
33296     },
33297     
33298     validate : function()
33299     {
33300         var d = this.dayField.validate();
33301         var m = this.monthField.validate();
33302         var y = this.yearField.validate();
33303         
33304         var valid = true;
33305         
33306         if(
33307                 (!this.dayAllowBlank && !d) ||
33308                 (!this.monthAllowBlank && !m) ||
33309                 (!this.yearAllowBlank && !y)
33310         ){
33311             valid = false;
33312         }
33313         
33314         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33315             return valid;
33316         }
33317         
33318         if(valid){
33319             this.markValid();
33320             return valid;
33321         }
33322         
33323         this.markInvalid();
33324         
33325         return valid;
33326     },
33327     
33328     markValid : function()
33329     {
33330         
33331         var label = this.el.select('label', true).first();
33332         var icon = this.el.select('i.fa-star', true).first();
33333
33334         if(label && icon){
33335             icon.remove();
33336         }
33337         
33338         this.fireEvent('valid', this);
33339     },
33340     
33341      /**
33342      * Mark this field as invalid
33343      * @param {String} msg The validation message
33344      */
33345     markInvalid : function(msg)
33346     {
33347         
33348         var label = this.el.select('label', true).first();
33349         var icon = this.el.select('i.fa-star', true).first();
33350
33351         if(label && !icon){
33352             this.el.select('.roo-date-split-field-label', true).createChild({
33353                 tag : 'i',
33354                 cls : 'text-danger fa fa-lg fa-star',
33355                 tooltip : 'This field is required',
33356                 style : 'margin-right:5px;'
33357             }, label, true);
33358         }
33359         
33360         this.fireEvent('invalid', this, msg);
33361     },
33362     
33363     clearInvalid : function()
33364     {
33365         var label = this.el.select('label', true).first();
33366         var icon = this.el.select('i.fa-star', true).first();
33367
33368         if(label && icon){
33369             icon.remove();
33370         }
33371         
33372         this.fireEvent('valid', this);
33373     },
33374     
33375     getName: function()
33376     {
33377         return this.name;
33378     }
33379     
33380 });
33381
33382  /**
33383  *
33384  * This is based on 
33385  * http://masonry.desandro.com
33386  *
33387  * The idea is to render all the bricks based on vertical width...
33388  *
33389  * The original code extends 'outlayer' - we might need to use that....
33390  * 
33391  */
33392
33393
33394 /**
33395  * @class Roo.bootstrap.LayoutMasonry
33396  * @extends Roo.bootstrap.Component
33397  * Bootstrap Layout Masonry class
33398  * 
33399  * @constructor
33400  * Create a new Element
33401  * @param {Object} config The config object
33402  */
33403
33404 Roo.bootstrap.LayoutMasonry = function(config){
33405     
33406     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33407     
33408     this.bricks = [];
33409     
33410     Roo.bootstrap.LayoutMasonry.register(this);
33411     
33412     this.addEvents({
33413         // raw events
33414         /**
33415          * @event layout
33416          * Fire after layout the items
33417          * @param {Roo.bootstrap.LayoutMasonry} this
33418          * @param {Roo.EventObject} e
33419          */
33420         "layout" : true
33421     });
33422     
33423 };
33424
33425 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33426     
33427     /**
33428      * @cfg {Boolean} isLayoutInstant = no animation?
33429      */   
33430     isLayoutInstant : false, // needed?
33431    
33432     /**
33433      * @cfg {Number} boxWidth  width of the columns
33434      */   
33435     boxWidth : 450,
33436     
33437       /**
33438      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33439      */   
33440     boxHeight : 0,
33441     
33442     /**
33443      * @cfg {Number} padWidth padding below box..
33444      */   
33445     padWidth : 10, 
33446     
33447     /**
33448      * @cfg {Number} gutter gutter width..
33449      */   
33450     gutter : 10,
33451     
33452      /**
33453      * @cfg {Number} maxCols maximum number of columns
33454      */   
33455     
33456     maxCols: 0,
33457     
33458     /**
33459      * @cfg {Boolean} isAutoInitial defalut true
33460      */   
33461     isAutoInitial : true, 
33462     
33463     containerWidth: 0,
33464     
33465     /**
33466      * @cfg {Boolean} isHorizontal defalut false
33467      */   
33468     isHorizontal : false, 
33469
33470     currentSize : null,
33471     
33472     tag: 'div',
33473     
33474     cls: '',
33475     
33476     bricks: null, //CompositeElement
33477     
33478     cols : 1,
33479     
33480     _isLayoutInited : false,
33481     
33482 //    isAlternative : false, // only use for vertical layout...
33483     
33484     /**
33485      * @cfg {Number} alternativePadWidth padding below box..
33486      */   
33487     alternativePadWidth : 50,
33488     
33489     selectedBrick : [],
33490     
33491     getAutoCreate : function(){
33492         
33493         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33494         
33495         var cfg = {
33496             tag: this.tag,
33497             cls: 'blog-masonary-wrapper ' + this.cls,
33498             cn : {
33499                 cls : 'mas-boxes masonary'
33500             }
33501         };
33502         
33503         return cfg;
33504     },
33505     
33506     getChildContainer: function( )
33507     {
33508         if (this.boxesEl) {
33509             return this.boxesEl;
33510         }
33511         
33512         this.boxesEl = this.el.select('.mas-boxes').first();
33513         
33514         return this.boxesEl;
33515     },
33516     
33517     
33518     initEvents : function()
33519     {
33520         var _this = this;
33521         
33522         if(this.isAutoInitial){
33523             Roo.log('hook children rendered');
33524             this.on('childrenrendered', function() {
33525                 Roo.log('children rendered');
33526                 _this.initial();
33527             } ,this);
33528         }
33529     },
33530     
33531     initial : function()
33532     {
33533         this.selectedBrick = [];
33534         
33535         this.currentSize = this.el.getBox(true);
33536         
33537         Roo.EventManager.onWindowResize(this.resize, this); 
33538
33539         if(!this.isAutoInitial){
33540             this.layout();
33541             return;
33542         }
33543         
33544         this.layout();
33545         
33546         return;
33547         //this.layout.defer(500,this);
33548         
33549     },
33550     
33551     resize : function()
33552     {
33553         var cs = this.el.getBox(true);
33554         
33555         if (
33556                 this.currentSize.width == cs.width && 
33557                 this.currentSize.x == cs.x && 
33558                 this.currentSize.height == cs.height && 
33559                 this.currentSize.y == cs.y 
33560         ) {
33561             Roo.log("no change in with or X or Y");
33562             return;
33563         }
33564         
33565         this.currentSize = cs;
33566         
33567         this.layout();
33568         
33569     },
33570     
33571     layout : function()
33572     {   
33573         this._resetLayout();
33574         
33575         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33576         
33577         this.layoutItems( isInstant );
33578       
33579         this._isLayoutInited = true;
33580         
33581         this.fireEvent('layout', this);
33582         
33583     },
33584     
33585     _resetLayout : function()
33586     {
33587         if(this.isHorizontal){
33588             this.horizontalMeasureColumns();
33589             return;
33590         }
33591         
33592         this.verticalMeasureColumns();
33593         
33594     },
33595     
33596     verticalMeasureColumns : function()
33597     {
33598         this.getContainerWidth();
33599         
33600 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33601 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33602 //            return;
33603 //        }
33604         
33605         var boxWidth = this.boxWidth + this.padWidth;
33606         
33607         if(this.containerWidth < this.boxWidth){
33608             boxWidth = this.containerWidth
33609         }
33610         
33611         var containerWidth = this.containerWidth;
33612         
33613         var cols = Math.floor(containerWidth / boxWidth);
33614         
33615         this.cols = Math.max( cols, 1 );
33616         
33617         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33618         
33619         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33620         
33621         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33622         
33623         this.colWidth = boxWidth + avail - this.padWidth;
33624         
33625         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33626         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33627     },
33628     
33629     horizontalMeasureColumns : function()
33630     {
33631         this.getContainerWidth();
33632         
33633         var boxWidth = this.boxWidth;
33634         
33635         if(this.containerWidth < boxWidth){
33636             boxWidth = this.containerWidth;
33637         }
33638         
33639         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33640         
33641         this.el.setHeight(boxWidth);
33642         
33643     },
33644     
33645     getContainerWidth : function()
33646     {
33647         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33648     },
33649     
33650     layoutItems : function( isInstant )
33651     {
33652         Roo.log(this.bricks);
33653         
33654         var items = Roo.apply([], this.bricks);
33655         
33656         if(this.isHorizontal){
33657             this._horizontalLayoutItems( items , isInstant );
33658             return;
33659         }
33660         
33661 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33662 //            this._verticalAlternativeLayoutItems( items , isInstant );
33663 //            return;
33664 //        }
33665         
33666         this._verticalLayoutItems( items , isInstant );
33667         
33668     },
33669     
33670     _verticalLayoutItems : function ( items , isInstant)
33671     {
33672         if ( !items || !items.length ) {
33673             return;
33674         }
33675         
33676         var standard = [
33677             ['xs', 'xs', 'xs', 'tall'],
33678             ['xs', 'xs', 'tall'],
33679             ['xs', 'xs', 'sm'],
33680             ['xs', 'xs', 'xs'],
33681             ['xs', 'tall'],
33682             ['xs', 'sm'],
33683             ['xs', 'xs'],
33684             ['xs'],
33685             
33686             ['sm', 'xs', 'xs'],
33687             ['sm', 'xs'],
33688             ['sm'],
33689             
33690             ['tall', 'xs', 'xs', 'xs'],
33691             ['tall', 'xs', 'xs'],
33692             ['tall', 'xs'],
33693             ['tall']
33694             
33695         ];
33696         
33697         var queue = [];
33698         
33699         var boxes = [];
33700         
33701         var box = [];
33702         
33703         Roo.each(items, function(item, k){
33704             
33705             switch (item.size) {
33706                 // these layouts take up a full box,
33707                 case 'md' :
33708                 case 'md-left' :
33709                 case 'md-right' :
33710                 case 'wide' :
33711                     
33712                     if(box.length){
33713                         boxes.push(box);
33714                         box = [];
33715                     }
33716                     
33717                     boxes.push([item]);
33718                     
33719                     break;
33720                     
33721                 case 'xs' :
33722                 case 'sm' :
33723                 case 'tall' :
33724                     
33725                     box.push(item);
33726                     
33727                     break;
33728                 default :
33729                     break;
33730                     
33731             }
33732             
33733         }, this);
33734         
33735         if(box.length){
33736             boxes.push(box);
33737             box = [];
33738         }
33739         
33740         var filterPattern = function(box, length)
33741         {
33742             if(!box.length){
33743                 return;
33744             }
33745             
33746             var match = false;
33747             
33748             var pattern = box.slice(0, length);
33749             
33750             var format = [];
33751             
33752             Roo.each(pattern, function(i){
33753                 format.push(i.size);
33754             }, this);
33755             
33756             Roo.each(standard, function(s){
33757                 
33758                 if(String(s) != String(format)){
33759                     return;
33760                 }
33761                 
33762                 match = true;
33763                 return false;
33764                 
33765             }, this);
33766             
33767             if(!match && length == 1){
33768                 return;
33769             }
33770             
33771             if(!match){
33772                 filterPattern(box, length - 1);
33773                 return;
33774             }
33775                 
33776             queue.push(pattern);
33777
33778             box = box.slice(length, box.length);
33779
33780             filterPattern(box, 4);
33781
33782             return;
33783             
33784         }
33785         
33786         Roo.each(boxes, function(box, k){
33787             
33788             if(!box.length){
33789                 return;
33790             }
33791             
33792             if(box.length == 1){
33793                 queue.push(box);
33794                 return;
33795             }
33796             
33797             filterPattern(box, 4);
33798             
33799         }, this);
33800         
33801         this._processVerticalLayoutQueue( queue, isInstant );
33802         
33803     },
33804     
33805 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33806 //    {
33807 //        if ( !items || !items.length ) {
33808 //            return;
33809 //        }
33810 //
33811 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33812 //        
33813 //    },
33814     
33815     _horizontalLayoutItems : function ( items , isInstant)
33816     {
33817         if ( !items || !items.length || items.length < 3) {
33818             return;
33819         }
33820         
33821         items.reverse();
33822         
33823         var eItems = items.slice(0, 3);
33824         
33825         items = items.slice(3, items.length);
33826         
33827         var standard = [
33828             ['xs', 'xs', 'xs', 'wide'],
33829             ['xs', 'xs', 'wide'],
33830             ['xs', 'xs', 'sm'],
33831             ['xs', 'xs', 'xs'],
33832             ['xs', 'wide'],
33833             ['xs', 'sm'],
33834             ['xs', 'xs'],
33835             ['xs'],
33836             
33837             ['sm', 'xs', 'xs'],
33838             ['sm', 'xs'],
33839             ['sm'],
33840             
33841             ['wide', 'xs', 'xs', 'xs'],
33842             ['wide', 'xs', 'xs'],
33843             ['wide', 'xs'],
33844             ['wide'],
33845             
33846             ['wide-thin']
33847         ];
33848         
33849         var queue = [];
33850         
33851         var boxes = [];
33852         
33853         var box = [];
33854         
33855         Roo.each(items, function(item, k){
33856             
33857             switch (item.size) {
33858                 case 'md' :
33859                 case 'md-left' :
33860                 case 'md-right' :
33861                 case 'tall' :
33862                     
33863                     if(box.length){
33864                         boxes.push(box);
33865                         box = [];
33866                     }
33867                     
33868                     boxes.push([item]);
33869                     
33870                     break;
33871                     
33872                 case 'xs' :
33873                 case 'sm' :
33874                 case 'wide' :
33875                 case 'wide-thin' :
33876                     
33877                     box.push(item);
33878                     
33879                     break;
33880                 default :
33881                     break;
33882                     
33883             }
33884             
33885         }, this);
33886         
33887         if(box.length){
33888             boxes.push(box);
33889             box = [];
33890         }
33891         
33892         var filterPattern = function(box, length)
33893         {
33894             if(!box.length){
33895                 return;
33896             }
33897             
33898             var match = false;
33899             
33900             var pattern = box.slice(0, length);
33901             
33902             var format = [];
33903             
33904             Roo.each(pattern, function(i){
33905                 format.push(i.size);
33906             }, this);
33907             
33908             Roo.each(standard, function(s){
33909                 
33910                 if(String(s) != String(format)){
33911                     return;
33912                 }
33913                 
33914                 match = true;
33915                 return false;
33916                 
33917             }, this);
33918             
33919             if(!match && length == 1){
33920                 return;
33921             }
33922             
33923             if(!match){
33924                 filterPattern(box, length - 1);
33925                 return;
33926             }
33927                 
33928             queue.push(pattern);
33929
33930             box = box.slice(length, box.length);
33931
33932             filterPattern(box, 4);
33933
33934             return;
33935             
33936         }
33937         
33938         Roo.each(boxes, function(box, k){
33939             
33940             if(!box.length){
33941                 return;
33942             }
33943             
33944             if(box.length == 1){
33945                 queue.push(box);
33946                 return;
33947             }
33948             
33949             filterPattern(box, 4);
33950             
33951         }, this);
33952         
33953         
33954         var prune = [];
33955         
33956         var pos = this.el.getBox(true);
33957         
33958         var minX = pos.x;
33959         
33960         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33961         
33962         var hit_end = false;
33963         
33964         Roo.each(queue, function(box){
33965             
33966             if(hit_end){
33967                 
33968                 Roo.each(box, function(b){
33969                 
33970                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33971                     b.el.hide();
33972
33973                 }, this);
33974
33975                 return;
33976             }
33977             
33978             var mx = 0;
33979             
33980             Roo.each(box, function(b){
33981                 
33982                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33983                 b.el.show();
33984
33985                 mx = Math.max(mx, b.x);
33986                 
33987             }, this);
33988             
33989             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33990             
33991             if(maxX < minX){
33992                 
33993                 Roo.each(box, function(b){
33994                 
33995                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33996                     b.el.hide();
33997                     
33998                 }, this);
33999                 
34000                 hit_end = true;
34001                 
34002                 return;
34003             }
34004             
34005             prune.push(box);
34006             
34007         }, this);
34008         
34009         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34010     },
34011     
34012     /** Sets position of item in DOM
34013     * @param {Element} item
34014     * @param {Number} x - horizontal position
34015     * @param {Number} y - vertical position
34016     * @param {Boolean} isInstant - disables transitions
34017     */
34018     _processVerticalLayoutQueue : function( queue, isInstant )
34019     {
34020         var pos = this.el.getBox(true);
34021         var x = pos.x;
34022         var y = pos.y;
34023         var maxY = [];
34024         
34025         for (var i = 0; i < this.cols; i++){
34026             maxY[i] = pos.y;
34027         }
34028         
34029         Roo.each(queue, function(box, k){
34030             
34031             var col = k % this.cols;
34032             
34033             Roo.each(box, function(b,kk){
34034                 
34035                 b.el.position('absolute');
34036                 
34037                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34038                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34039                 
34040                 if(b.size == 'md-left' || b.size == 'md-right'){
34041                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34042                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34043                 }
34044                 
34045                 b.el.setWidth(width);
34046                 b.el.setHeight(height);
34047                 // iframe?
34048                 b.el.select('iframe',true).setSize(width,height);
34049                 
34050             }, this);
34051             
34052             for (var i = 0; i < this.cols; i++){
34053                 
34054                 if(maxY[i] < maxY[col]){
34055                     col = i;
34056                     continue;
34057                 }
34058                 
34059                 col = Math.min(col, i);
34060                 
34061             }
34062             
34063             x = pos.x + col * (this.colWidth + this.padWidth);
34064             
34065             y = maxY[col];
34066             
34067             var positions = [];
34068             
34069             switch (box.length){
34070                 case 1 :
34071                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34072                     break;
34073                 case 2 :
34074                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34075                     break;
34076                 case 3 :
34077                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34078                     break;
34079                 case 4 :
34080                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34081                     break;
34082                 default :
34083                     break;
34084             }
34085             
34086             Roo.each(box, function(b,kk){
34087                 
34088                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34089                 
34090                 var sz = b.el.getSize();
34091                 
34092                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34093                 
34094             }, this);
34095             
34096         }, this);
34097         
34098         var mY = 0;
34099         
34100         for (var i = 0; i < this.cols; i++){
34101             mY = Math.max(mY, maxY[i]);
34102         }
34103         
34104         this.el.setHeight(mY - pos.y);
34105         
34106     },
34107     
34108 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34109 //    {
34110 //        var pos = this.el.getBox(true);
34111 //        var x = pos.x;
34112 //        var y = pos.y;
34113 //        var maxX = pos.right;
34114 //        
34115 //        var maxHeight = 0;
34116 //        
34117 //        Roo.each(items, function(item, k){
34118 //            
34119 //            var c = k % 2;
34120 //            
34121 //            item.el.position('absolute');
34122 //                
34123 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34124 //
34125 //            item.el.setWidth(width);
34126 //
34127 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34128 //
34129 //            item.el.setHeight(height);
34130 //            
34131 //            if(c == 0){
34132 //                item.el.setXY([x, y], isInstant ? false : true);
34133 //            } else {
34134 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34135 //            }
34136 //            
34137 //            y = y + height + this.alternativePadWidth;
34138 //            
34139 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34140 //            
34141 //        }, this);
34142 //        
34143 //        this.el.setHeight(maxHeight);
34144 //        
34145 //    },
34146     
34147     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34148     {
34149         var pos = this.el.getBox(true);
34150         
34151         var minX = pos.x;
34152         var minY = pos.y;
34153         
34154         var maxX = pos.right;
34155         
34156         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34157         
34158         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34159         
34160         Roo.each(queue, function(box, k){
34161             
34162             Roo.each(box, function(b, kk){
34163                 
34164                 b.el.position('absolute');
34165                 
34166                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34167                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34168                 
34169                 if(b.size == 'md-left' || b.size == 'md-right'){
34170                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34171                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34172                 }
34173                 
34174                 b.el.setWidth(width);
34175                 b.el.setHeight(height);
34176                 
34177             }, this);
34178             
34179             if(!box.length){
34180                 return;
34181             }
34182             
34183             var positions = [];
34184             
34185             switch (box.length){
34186                 case 1 :
34187                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34188                     break;
34189                 case 2 :
34190                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34191                     break;
34192                 case 3 :
34193                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34194                     break;
34195                 case 4 :
34196                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34197                     break;
34198                 default :
34199                     break;
34200             }
34201             
34202             Roo.each(box, function(b,kk){
34203                 
34204                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34205                 
34206                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34207                 
34208             }, this);
34209             
34210         }, this);
34211         
34212     },
34213     
34214     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34215     {
34216         Roo.each(eItems, function(b,k){
34217             
34218             b.size = (k == 0) ? 'sm' : 'xs';
34219             b.x = (k == 0) ? 2 : 1;
34220             b.y = (k == 0) ? 2 : 1;
34221             
34222             b.el.position('absolute');
34223             
34224             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34225                 
34226             b.el.setWidth(width);
34227             
34228             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34229             
34230             b.el.setHeight(height);
34231             
34232         }, this);
34233
34234         var positions = [];
34235         
34236         positions.push({
34237             x : maxX - this.unitWidth * 2 - this.gutter,
34238             y : minY
34239         });
34240         
34241         positions.push({
34242             x : maxX - this.unitWidth,
34243             y : minY + (this.unitWidth + this.gutter) * 2
34244         });
34245         
34246         positions.push({
34247             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34248             y : minY
34249         });
34250         
34251         Roo.each(eItems, function(b,k){
34252             
34253             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34254
34255         }, this);
34256         
34257     },
34258     
34259     getVerticalOneBoxColPositions : function(x, y, box)
34260     {
34261         var pos = [];
34262         
34263         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34264         
34265         if(box[0].size == 'md-left'){
34266             rand = 0;
34267         }
34268         
34269         if(box[0].size == 'md-right'){
34270             rand = 1;
34271         }
34272         
34273         pos.push({
34274             x : x + (this.unitWidth + this.gutter) * rand,
34275             y : y
34276         });
34277         
34278         return pos;
34279     },
34280     
34281     getVerticalTwoBoxColPositions : function(x, y, box)
34282     {
34283         var pos = [];
34284         
34285         if(box[0].size == 'xs'){
34286             
34287             pos.push({
34288                 x : x,
34289                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34290             });
34291
34292             pos.push({
34293                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34294                 y : y
34295             });
34296             
34297             return pos;
34298             
34299         }
34300         
34301         pos.push({
34302             x : x,
34303             y : y
34304         });
34305
34306         pos.push({
34307             x : x + (this.unitWidth + this.gutter) * 2,
34308             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34309         });
34310         
34311         return pos;
34312         
34313     },
34314     
34315     getVerticalThreeBoxColPositions : function(x, y, box)
34316     {
34317         var pos = [];
34318         
34319         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34320             
34321             pos.push({
34322                 x : x,
34323                 y : y
34324             });
34325
34326             pos.push({
34327                 x : x + (this.unitWidth + this.gutter) * 1,
34328                 y : y
34329             });
34330             
34331             pos.push({
34332                 x : x + (this.unitWidth + this.gutter) * 2,
34333                 y : y
34334             });
34335             
34336             return pos;
34337             
34338         }
34339         
34340         if(box[0].size == 'xs' && box[1].size == 'xs'){
34341             
34342             pos.push({
34343                 x : x,
34344                 y : y
34345             });
34346
34347             pos.push({
34348                 x : x,
34349                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34350             });
34351             
34352             pos.push({
34353                 x : x + (this.unitWidth + this.gutter) * 1,
34354                 y : y
34355             });
34356             
34357             return pos;
34358             
34359         }
34360         
34361         pos.push({
34362             x : x,
34363             y : y
34364         });
34365
34366         pos.push({
34367             x : x + (this.unitWidth + this.gutter) * 2,
34368             y : y
34369         });
34370
34371         pos.push({
34372             x : x + (this.unitWidth + this.gutter) * 2,
34373             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34374         });
34375             
34376         return pos;
34377         
34378     },
34379     
34380     getVerticalFourBoxColPositions : function(x, y, box)
34381     {
34382         var pos = [];
34383         
34384         if(box[0].size == 'xs'){
34385             
34386             pos.push({
34387                 x : x,
34388                 y : y
34389             });
34390
34391             pos.push({
34392                 x : x,
34393                 y : y + (this.unitHeight + this.gutter) * 1
34394             });
34395             
34396             pos.push({
34397                 x : x,
34398                 y : y + (this.unitHeight + this.gutter) * 2
34399             });
34400             
34401             pos.push({
34402                 x : x + (this.unitWidth + this.gutter) * 1,
34403                 y : y
34404             });
34405             
34406             return pos;
34407             
34408         }
34409         
34410         pos.push({
34411             x : x,
34412             y : y
34413         });
34414
34415         pos.push({
34416             x : x + (this.unitWidth + this.gutter) * 2,
34417             y : y
34418         });
34419
34420         pos.push({
34421             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34422             y : y + (this.unitHeight + this.gutter) * 1
34423         });
34424
34425         pos.push({
34426             x : x + (this.unitWidth + this.gutter) * 2,
34427             y : y + (this.unitWidth + this.gutter) * 2
34428         });
34429
34430         return pos;
34431         
34432     },
34433     
34434     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34435     {
34436         var pos = [];
34437         
34438         if(box[0].size == 'md-left'){
34439             pos.push({
34440                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34441                 y : minY
34442             });
34443             
34444             return pos;
34445         }
34446         
34447         if(box[0].size == 'md-right'){
34448             pos.push({
34449                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34450                 y : minY + (this.unitWidth + this.gutter) * 1
34451             });
34452             
34453             return pos;
34454         }
34455         
34456         var rand = Math.floor(Math.random() * (4 - box[0].y));
34457         
34458         pos.push({
34459             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34460             y : minY + (this.unitWidth + this.gutter) * rand
34461         });
34462         
34463         return pos;
34464         
34465     },
34466     
34467     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34468     {
34469         var pos = [];
34470         
34471         if(box[0].size == 'xs'){
34472             
34473             pos.push({
34474                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34475                 y : minY
34476             });
34477
34478             pos.push({
34479                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34480                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34481             });
34482             
34483             return pos;
34484             
34485         }
34486         
34487         pos.push({
34488             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34489             y : minY
34490         });
34491
34492         pos.push({
34493             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34494             y : minY + (this.unitWidth + this.gutter) * 2
34495         });
34496         
34497         return pos;
34498         
34499     },
34500     
34501     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34502     {
34503         var pos = [];
34504         
34505         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34506             
34507             pos.push({
34508                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34509                 y : minY
34510             });
34511
34512             pos.push({
34513                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34514                 y : minY + (this.unitWidth + this.gutter) * 1
34515             });
34516             
34517             pos.push({
34518                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34519                 y : minY + (this.unitWidth + this.gutter) * 2
34520             });
34521             
34522             return pos;
34523             
34524         }
34525         
34526         if(box[0].size == 'xs' && box[1].size == 'xs'){
34527             
34528             pos.push({
34529                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34530                 y : minY
34531             });
34532
34533             pos.push({
34534                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34535                 y : minY
34536             });
34537             
34538             pos.push({
34539                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34540                 y : minY + (this.unitWidth + this.gutter) * 1
34541             });
34542             
34543             return pos;
34544             
34545         }
34546         
34547         pos.push({
34548             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34549             y : minY
34550         });
34551
34552         pos.push({
34553             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34554             y : minY + (this.unitWidth + this.gutter) * 2
34555         });
34556
34557         pos.push({
34558             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34559             y : minY + (this.unitWidth + this.gutter) * 2
34560         });
34561             
34562         return pos;
34563         
34564     },
34565     
34566     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34567     {
34568         var pos = [];
34569         
34570         if(box[0].size == 'xs'){
34571             
34572             pos.push({
34573                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34574                 y : minY
34575             });
34576
34577             pos.push({
34578                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34579                 y : minY
34580             });
34581             
34582             pos.push({
34583                 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),
34584                 y : minY
34585             });
34586             
34587             pos.push({
34588                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34589                 y : minY + (this.unitWidth + this.gutter) * 1
34590             });
34591             
34592             return pos;
34593             
34594         }
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[1].x - this.gutter * (box[1].x - 1),
34603             y : minY + (this.unitWidth + this.gutter) * 2
34604         });
34605         
34606         pos.push({
34607             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34608             y : minY + (this.unitWidth + this.gutter) * 2
34609         });
34610         
34611         pos.push({
34612             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),
34613             y : minY + (this.unitWidth + this.gutter) * 2
34614         });
34615
34616         return pos;
34617         
34618     },
34619     
34620     /**
34621     * remove a Masonry Brick
34622     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34623     */
34624     removeBrick : function(brick_id)
34625     {
34626         if (!brick_id) {
34627             return;
34628         }
34629         
34630         for (var i = 0; i<this.bricks.length; i++) {
34631             if (this.bricks[i].id == brick_id) {
34632                 this.bricks.splice(i,1);
34633                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34634                 this.initial();
34635             }
34636         }
34637     },
34638     
34639     /**
34640     * adds a Masonry Brick
34641     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34642     */
34643     addBrick : function(cfg)
34644     {
34645         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34646         //this.register(cn);
34647         cn.parentId = this.id;
34648         cn.render(this.el);
34649         return cn;
34650     },
34651     
34652     /**
34653     * register a Masonry Brick
34654     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34655     */
34656     
34657     register : function(brick)
34658     {
34659         this.bricks.push(brick);
34660         brick.masonryId = this.id;
34661     },
34662     
34663     /**
34664     * clear all the Masonry Brick
34665     */
34666     clearAll : function()
34667     {
34668         this.bricks = [];
34669         //this.getChildContainer().dom.innerHTML = "";
34670         this.el.dom.innerHTML = '';
34671     },
34672     
34673     getSelected : function()
34674     {
34675         if (!this.selectedBrick) {
34676             return false;
34677         }
34678         
34679         return this.selectedBrick;
34680     }
34681 });
34682
34683 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34684     
34685     groups: {},
34686      /**
34687     * register a Masonry Layout
34688     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34689     */
34690     
34691     register : function(layout)
34692     {
34693         this.groups[layout.id] = layout;
34694     },
34695     /**
34696     * fetch a  Masonry Layout based on the masonry layout ID
34697     * @param {string} the masonry layout to add
34698     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34699     */
34700     
34701     get: function(layout_id) {
34702         if (typeof(this.groups[layout_id]) == 'undefined') {
34703             return false;
34704         }
34705         return this.groups[layout_id] ;
34706     }
34707     
34708     
34709     
34710 });
34711
34712  
34713
34714  /**
34715  *
34716  * This is based on 
34717  * http://masonry.desandro.com
34718  *
34719  * The idea is to render all the bricks based on vertical width...
34720  *
34721  * The original code extends 'outlayer' - we might need to use that....
34722  * 
34723  */
34724
34725
34726 /**
34727  * @class Roo.bootstrap.LayoutMasonryAuto
34728  * @extends Roo.bootstrap.Component
34729  * Bootstrap Layout Masonry class
34730  * 
34731  * @constructor
34732  * Create a new Element
34733  * @param {Object} config The config object
34734  */
34735
34736 Roo.bootstrap.LayoutMasonryAuto = function(config){
34737     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34738 };
34739
34740 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34741     
34742       /**
34743      * @cfg {Boolean} isFitWidth  - resize the width..
34744      */   
34745     isFitWidth : false,  // options..
34746     /**
34747      * @cfg {Boolean} isOriginLeft = left align?
34748      */   
34749     isOriginLeft : true,
34750     /**
34751      * @cfg {Boolean} isOriginTop = top align?
34752      */   
34753     isOriginTop : false,
34754     /**
34755      * @cfg {Boolean} isLayoutInstant = no animation?
34756      */   
34757     isLayoutInstant : false, // needed?
34758     /**
34759      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34760      */   
34761     isResizingContainer : true,
34762     /**
34763      * @cfg {Number} columnWidth  width of the columns 
34764      */   
34765     
34766     columnWidth : 0,
34767     
34768     /**
34769      * @cfg {Number} maxCols maximum number of columns
34770      */   
34771     
34772     maxCols: 0,
34773     /**
34774      * @cfg {Number} padHeight padding below box..
34775      */   
34776     
34777     padHeight : 10, 
34778     
34779     /**
34780      * @cfg {Boolean} isAutoInitial defalut true
34781      */   
34782     
34783     isAutoInitial : true, 
34784     
34785     // private?
34786     gutter : 0,
34787     
34788     containerWidth: 0,
34789     initialColumnWidth : 0,
34790     currentSize : null,
34791     
34792     colYs : null, // array.
34793     maxY : 0,
34794     padWidth: 10,
34795     
34796     
34797     tag: 'div',
34798     cls: '',
34799     bricks: null, //CompositeElement
34800     cols : 0, // array?
34801     // element : null, // wrapped now this.el
34802     _isLayoutInited : null, 
34803     
34804     
34805     getAutoCreate : function(){
34806         
34807         var cfg = {
34808             tag: this.tag,
34809             cls: 'blog-masonary-wrapper ' + this.cls,
34810             cn : {
34811                 cls : 'mas-boxes masonary'
34812             }
34813         };
34814         
34815         return cfg;
34816     },
34817     
34818     getChildContainer: function( )
34819     {
34820         if (this.boxesEl) {
34821             return this.boxesEl;
34822         }
34823         
34824         this.boxesEl = this.el.select('.mas-boxes').first();
34825         
34826         return this.boxesEl;
34827     },
34828     
34829     
34830     initEvents : function()
34831     {
34832         var _this = this;
34833         
34834         if(this.isAutoInitial){
34835             Roo.log('hook children rendered');
34836             this.on('childrenrendered', function() {
34837                 Roo.log('children rendered');
34838                 _this.initial();
34839             } ,this);
34840         }
34841         
34842     },
34843     
34844     initial : function()
34845     {
34846         this.reloadItems();
34847
34848         this.currentSize = this.el.getBox(true);
34849
34850         /// was window resize... - let's see if this works..
34851         Roo.EventManager.onWindowResize(this.resize, this); 
34852
34853         if(!this.isAutoInitial){
34854             this.layout();
34855             return;
34856         }
34857         
34858         this.layout.defer(500,this);
34859     },
34860     
34861     reloadItems: function()
34862     {
34863         this.bricks = this.el.select('.masonry-brick', true);
34864         
34865         this.bricks.each(function(b) {
34866             //Roo.log(b.getSize());
34867             if (!b.attr('originalwidth')) {
34868                 b.attr('originalwidth',  b.getSize().width);
34869             }
34870             
34871         });
34872         
34873         Roo.log(this.bricks.elements.length);
34874     },
34875     
34876     resize : function()
34877     {
34878         Roo.log('resize');
34879         var cs = this.el.getBox(true);
34880         
34881         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34882             Roo.log("no change in with or X");
34883             return;
34884         }
34885         this.currentSize = cs;
34886         this.layout();
34887     },
34888     
34889     layout : function()
34890     {
34891          Roo.log('layout');
34892         this._resetLayout();
34893         //this._manageStamps();
34894       
34895         // don't animate first layout
34896         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34897         this.layoutItems( isInstant );
34898       
34899         // flag for initalized
34900         this._isLayoutInited = true;
34901     },
34902     
34903     layoutItems : function( isInstant )
34904     {
34905         //var items = this._getItemsForLayout( this.items );
34906         // original code supports filtering layout items.. we just ignore it..
34907         
34908         this._layoutItems( this.bricks , isInstant );
34909       
34910         this._postLayout();
34911     },
34912     _layoutItems : function ( items , isInstant)
34913     {
34914        //this.fireEvent( 'layout', this, items );
34915     
34916
34917         if ( !items || !items.elements.length ) {
34918           // no items, emit event with empty array
34919             return;
34920         }
34921
34922         var queue = [];
34923         items.each(function(item) {
34924             Roo.log("layout item");
34925             Roo.log(item);
34926             // get x/y object from method
34927             var position = this._getItemLayoutPosition( item );
34928             // enqueue
34929             position.item = item;
34930             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34931             queue.push( position );
34932         }, this);
34933       
34934         this._processLayoutQueue( queue );
34935     },
34936     /** Sets position of item in DOM
34937     * @param {Element} item
34938     * @param {Number} x - horizontal position
34939     * @param {Number} y - vertical position
34940     * @param {Boolean} isInstant - disables transitions
34941     */
34942     _processLayoutQueue : function( queue )
34943     {
34944         for ( var i=0, len = queue.length; i < len; i++ ) {
34945             var obj = queue[i];
34946             obj.item.position('absolute');
34947             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34948         }
34949     },
34950       
34951     
34952     /**
34953     * Any logic you want to do after each layout,
34954     * i.e. size the container
34955     */
34956     _postLayout : function()
34957     {
34958         this.resizeContainer();
34959     },
34960     
34961     resizeContainer : function()
34962     {
34963         if ( !this.isResizingContainer ) {
34964             return;
34965         }
34966         var size = this._getContainerSize();
34967         if ( size ) {
34968             this.el.setSize(size.width,size.height);
34969             this.boxesEl.setSize(size.width,size.height);
34970         }
34971     },
34972     
34973     
34974     
34975     _resetLayout : function()
34976     {
34977         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34978         this.colWidth = this.el.getWidth();
34979         //this.gutter = this.el.getWidth(); 
34980         
34981         this.measureColumns();
34982
34983         // reset column Y
34984         var i = this.cols;
34985         this.colYs = [];
34986         while (i--) {
34987             this.colYs.push( 0 );
34988         }
34989     
34990         this.maxY = 0;
34991     },
34992
34993     measureColumns : function()
34994     {
34995         this.getContainerWidth();
34996       // if columnWidth is 0, default to outerWidth of first item
34997         if ( !this.columnWidth ) {
34998             var firstItem = this.bricks.first();
34999             Roo.log(firstItem);
35000             this.columnWidth  = this.containerWidth;
35001             if (firstItem && firstItem.attr('originalwidth') ) {
35002                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35003             }
35004             // columnWidth fall back to item of first element
35005             Roo.log("set column width?");
35006                         this.initialColumnWidth = this.columnWidth  ;
35007
35008             // if first elem has no width, default to size of container
35009             
35010         }
35011         
35012         
35013         if (this.initialColumnWidth) {
35014             this.columnWidth = this.initialColumnWidth;
35015         }
35016         
35017         
35018             
35019         // column width is fixed at the top - however if container width get's smaller we should
35020         // reduce it...
35021         
35022         // this bit calcs how man columns..
35023             
35024         var columnWidth = this.columnWidth += this.gutter;
35025       
35026         // calculate columns
35027         var containerWidth = this.containerWidth + this.gutter;
35028         
35029         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35030         // fix rounding errors, typically with gutters
35031         var excess = columnWidth - containerWidth % columnWidth;
35032         
35033         
35034         // if overshoot is less than a pixel, round up, otherwise floor it
35035         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35036         cols = Math[ mathMethod ]( cols );
35037         this.cols = Math.max( cols, 1 );
35038         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35039         
35040          // padding positioning..
35041         var totalColWidth = this.cols * this.columnWidth;
35042         var padavail = this.containerWidth - totalColWidth;
35043         // so for 2 columns - we need 3 'pads'
35044         
35045         var padNeeded = (1+this.cols) * this.padWidth;
35046         
35047         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35048         
35049         this.columnWidth += padExtra
35050         //this.padWidth = Math.floor(padavail /  ( this.cols));
35051         
35052         // adjust colum width so that padding is fixed??
35053         
35054         // we have 3 columns ... total = width * 3
35055         // we have X left over... that should be used by 
35056         
35057         //if (this.expandC) {
35058             
35059         //}
35060         
35061         
35062         
35063     },
35064     
35065     getContainerWidth : function()
35066     {
35067        /* // container is parent if fit width
35068         var container = this.isFitWidth ? this.element.parentNode : this.element;
35069         // check that this.size and size are there
35070         // IE8 triggers resize on body size change, so they might not be
35071         
35072         var size = getSize( container );  //FIXME
35073         this.containerWidth = size && size.innerWidth; //FIXME
35074         */
35075          
35076         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35077         
35078     },
35079     
35080     _getItemLayoutPosition : function( item )  // what is item?
35081     {
35082         // we resize the item to our columnWidth..
35083       
35084         item.setWidth(this.columnWidth);
35085         item.autoBoxAdjust  = false;
35086         
35087         var sz = item.getSize();
35088  
35089         // how many columns does this brick span
35090         var remainder = this.containerWidth % this.columnWidth;
35091         
35092         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35093         // round if off by 1 pixel, otherwise use ceil
35094         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35095         colSpan = Math.min( colSpan, this.cols );
35096         
35097         // normally this should be '1' as we dont' currently allow multi width columns..
35098         
35099         var colGroup = this._getColGroup( colSpan );
35100         // get the minimum Y value from the columns
35101         var minimumY = Math.min.apply( Math, colGroup );
35102         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35103         
35104         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35105          
35106         // position the brick
35107         var position = {
35108             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35109             y: this.currentSize.y + minimumY + this.padHeight
35110         };
35111         
35112         Roo.log(position);
35113         // apply setHeight to necessary columns
35114         var setHeight = minimumY + sz.height + this.padHeight;
35115         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35116         
35117         var setSpan = this.cols + 1 - colGroup.length;
35118         for ( var i = 0; i < setSpan; i++ ) {
35119           this.colYs[ shortColIndex + i ] = setHeight ;
35120         }
35121       
35122         return position;
35123     },
35124     
35125     /**
35126      * @param {Number} colSpan - number of columns the element spans
35127      * @returns {Array} colGroup
35128      */
35129     _getColGroup : function( colSpan )
35130     {
35131         if ( colSpan < 2 ) {
35132           // if brick spans only one column, use all the column Ys
35133           return this.colYs;
35134         }
35135       
35136         var colGroup = [];
35137         // how many different places could this brick fit horizontally
35138         var groupCount = this.cols + 1 - colSpan;
35139         // for each group potential horizontal position
35140         for ( var i = 0; i < groupCount; i++ ) {
35141           // make an array of colY values for that one group
35142           var groupColYs = this.colYs.slice( i, i + colSpan );
35143           // and get the max value of the array
35144           colGroup[i] = Math.max.apply( Math, groupColYs );
35145         }
35146         return colGroup;
35147     },
35148     /*
35149     _manageStamp : function( stamp )
35150     {
35151         var stampSize =  stamp.getSize();
35152         var offset = stamp.getBox();
35153         // get the columns that this stamp affects
35154         var firstX = this.isOriginLeft ? offset.x : offset.right;
35155         var lastX = firstX + stampSize.width;
35156         var firstCol = Math.floor( firstX / this.columnWidth );
35157         firstCol = Math.max( 0, firstCol );
35158         
35159         var lastCol = Math.floor( lastX / this.columnWidth );
35160         // lastCol should not go over if multiple of columnWidth #425
35161         lastCol -= lastX % this.columnWidth ? 0 : 1;
35162         lastCol = Math.min( this.cols - 1, lastCol );
35163         
35164         // set colYs to bottom of the stamp
35165         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35166             stampSize.height;
35167             
35168         for ( var i = firstCol; i <= lastCol; i++ ) {
35169           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35170         }
35171     },
35172     */
35173     
35174     _getContainerSize : function()
35175     {
35176         this.maxY = Math.max.apply( Math, this.colYs );
35177         var size = {
35178             height: this.maxY
35179         };
35180       
35181         if ( this.isFitWidth ) {
35182             size.width = this._getContainerFitWidth();
35183         }
35184       
35185         return size;
35186     },
35187     
35188     _getContainerFitWidth : function()
35189     {
35190         var unusedCols = 0;
35191         // count unused columns
35192         var i = this.cols;
35193         while ( --i ) {
35194           if ( this.colYs[i] !== 0 ) {
35195             break;
35196           }
35197           unusedCols++;
35198         }
35199         // fit container to columns that have been used
35200         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35201     },
35202     
35203     needsResizeLayout : function()
35204     {
35205         var previousWidth = this.containerWidth;
35206         this.getContainerWidth();
35207         return previousWidth !== this.containerWidth;
35208     }
35209  
35210 });
35211
35212  
35213
35214  /*
35215  * - LGPL
35216  *
35217  * element
35218  * 
35219  */
35220
35221 /**
35222  * @class Roo.bootstrap.MasonryBrick
35223  * @extends Roo.bootstrap.Component
35224  * Bootstrap MasonryBrick class
35225  * 
35226  * @constructor
35227  * Create a new MasonryBrick
35228  * @param {Object} config The config object
35229  */
35230
35231 Roo.bootstrap.MasonryBrick = function(config){
35232     
35233     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35234     
35235     Roo.bootstrap.MasonryBrick.register(this);
35236     
35237     this.addEvents({
35238         // raw events
35239         /**
35240          * @event click
35241          * When a MasonryBrick is clcik
35242          * @param {Roo.bootstrap.MasonryBrick} this
35243          * @param {Roo.EventObject} e
35244          */
35245         "click" : true
35246     });
35247 };
35248
35249 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35250     
35251     /**
35252      * @cfg {String} title
35253      */   
35254     title : '',
35255     /**
35256      * @cfg {String} html
35257      */   
35258     html : '',
35259     /**
35260      * @cfg {String} bgimage
35261      */   
35262     bgimage : '',
35263     /**
35264      * @cfg {String} videourl
35265      */   
35266     videourl : '',
35267     /**
35268      * @cfg {String} cls
35269      */   
35270     cls : '',
35271     /**
35272      * @cfg {String} href
35273      */   
35274     href : '',
35275     /**
35276      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35277      */   
35278     size : 'xs',
35279     
35280     /**
35281      * @cfg {String} placetitle (center|bottom)
35282      */   
35283     placetitle : '',
35284     
35285     /**
35286      * @cfg {Boolean} isFitContainer defalut true
35287      */   
35288     isFitContainer : true, 
35289     
35290     /**
35291      * @cfg {Boolean} preventDefault defalut false
35292      */   
35293     preventDefault : false, 
35294     
35295     /**
35296      * @cfg {Boolean} inverse defalut false
35297      */   
35298     maskInverse : false, 
35299     
35300     getAutoCreate : function()
35301     {
35302         if(!this.isFitContainer){
35303             return this.getSplitAutoCreate();
35304         }
35305         
35306         var cls = 'masonry-brick masonry-brick-full';
35307         
35308         if(this.href.length){
35309             cls += ' masonry-brick-link';
35310         }
35311         
35312         if(this.bgimage.length){
35313             cls += ' masonry-brick-image';
35314         }
35315         
35316         if(this.maskInverse){
35317             cls += ' mask-inverse';
35318         }
35319         
35320         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35321             cls += ' enable-mask';
35322         }
35323         
35324         if(this.size){
35325             cls += ' masonry-' + this.size + '-brick';
35326         }
35327         
35328         if(this.placetitle.length){
35329             
35330             switch (this.placetitle) {
35331                 case 'center' :
35332                     cls += ' masonry-center-title';
35333                     break;
35334                 case 'bottom' :
35335                     cls += ' masonry-bottom-title';
35336                     break;
35337                 default:
35338                     break;
35339             }
35340             
35341         } else {
35342             if(!this.html.length && !this.bgimage.length){
35343                 cls += ' masonry-center-title';
35344             }
35345
35346             if(!this.html.length && this.bgimage.length){
35347                 cls += ' masonry-bottom-title';
35348             }
35349         }
35350         
35351         if(this.cls){
35352             cls += ' ' + this.cls;
35353         }
35354         
35355         var cfg = {
35356             tag: (this.href.length) ? 'a' : 'div',
35357             cls: cls,
35358             cn: [
35359                 {
35360                     tag: 'div',
35361                     cls: 'masonry-brick-mask'
35362                 },
35363                 {
35364                     tag: 'div',
35365                     cls: 'masonry-brick-paragraph',
35366                     cn: []
35367                 }
35368             ]
35369         };
35370         
35371         if(this.href.length){
35372             cfg.href = this.href;
35373         }
35374         
35375         var cn = cfg.cn[1].cn;
35376         
35377         if(this.title.length){
35378             cn.push({
35379                 tag: 'h4',
35380                 cls: 'masonry-brick-title',
35381                 html: this.title
35382             });
35383         }
35384         
35385         if(this.html.length){
35386             cn.push({
35387                 tag: 'p',
35388                 cls: 'masonry-brick-text',
35389                 html: this.html
35390             });
35391         }
35392         
35393         if (!this.title.length && !this.html.length) {
35394             cfg.cn[1].cls += ' hide';
35395         }
35396         
35397         if(this.bgimage.length){
35398             cfg.cn.push({
35399                 tag: 'img',
35400                 cls: 'masonry-brick-image-view',
35401                 src: this.bgimage
35402             });
35403         }
35404         
35405         if(this.videourl.length){
35406             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35407             // youtube support only?
35408             cfg.cn.push({
35409                 tag: 'iframe',
35410                 cls: 'masonry-brick-image-view',
35411                 src: vurl,
35412                 frameborder : 0,
35413                 allowfullscreen : true
35414             });
35415         }
35416         
35417         return cfg;
35418         
35419     },
35420     
35421     getSplitAutoCreate : function()
35422     {
35423         var cls = 'masonry-brick masonry-brick-split';
35424         
35425         if(this.href.length){
35426             cls += ' masonry-brick-link';
35427         }
35428         
35429         if(this.bgimage.length){
35430             cls += ' masonry-brick-image';
35431         }
35432         
35433         if(this.size){
35434             cls += ' masonry-' + this.size + '-brick';
35435         }
35436         
35437         switch (this.placetitle) {
35438             case 'center' :
35439                 cls += ' masonry-center-title';
35440                 break;
35441             case 'bottom' :
35442                 cls += ' masonry-bottom-title';
35443                 break;
35444             default:
35445                 if(!this.bgimage.length){
35446                     cls += ' masonry-center-title';
35447                 }
35448
35449                 if(this.bgimage.length){
35450                     cls += ' masonry-bottom-title';
35451                 }
35452                 break;
35453         }
35454         
35455         if(this.cls){
35456             cls += ' ' + this.cls;
35457         }
35458         
35459         var cfg = {
35460             tag: (this.href.length) ? 'a' : 'div',
35461             cls: cls,
35462             cn: [
35463                 {
35464                     tag: 'div',
35465                     cls: 'masonry-brick-split-head',
35466                     cn: [
35467                         {
35468                             tag: 'div',
35469                             cls: 'masonry-brick-paragraph',
35470                             cn: []
35471                         }
35472                     ]
35473                 },
35474                 {
35475                     tag: 'div',
35476                     cls: 'masonry-brick-split-body',
35477                     cn: []
35478                 }
35479             ]
35480         };
35481         
35482         if(this.href.length){
35483             cfg.href = this.href;
35484         }
35485         
35486         if(this.title.length){
35487             cfg.cn[0].cn[0].cn.push({
35488                 tag: 'h4',
35489                 cls: 'masonry-brick-title',
35490                 html: this.title
35491             });
35492         }
35493         
35494         if(this.html.length){
35495             cfg.cn[1].cn.push({
35496                 tag: 'p',
35497                 cls: 'masonry-brick-text',
35498                 html: this.html
35499             });
35500         }
35501
35502         if(this.bgimage.length){
35503             cfg.cn[0].cn.push({
35504                 tag: 'img',
35505                 cls: 'masonry-brick-image-view',
35506                 src: this.bgimage
35507             });
35508         }
35509         
35510         if(this.videourl.length){
35511             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35512             // youtube support only?
35513             cfg.cn[0].cn.cn.push({
35514                 tag: 'iframe',
35515                 cls: 'masonry-brick-image-view',
35516                 src: vurl,
35517                 frameborder : 0,
35518                 allowfullscreen : true
35519             });
35520         }
35521         
35522         return cfg;
35523     },
35524     
35525     initEvents: function() 
35526     {
35527         switch (this.size) {
35528             case 'xs' :
35529                 this.x = 1;
35530                 this.y = 1;
35531                 break;
35532             case 'sm' :
35533                 this.x = 2;
35534                 this.y = 2;
35535                 break;
35536             case 'md' :
35537             case 'md-left' :
35538             case 'md-right' :
35539                 this.x = 3;
35540                 this.y = 3;
35541                 break;
35542             case 'tall' :
35543                 this.x = 2;
35544                 this.y = 3;
35545                 break;
35546             case 'wide' :
35547                 this.x = 3;
35548                 this.y = 2;
35549                 break;
35550             case 'wide-thin' :
35551                 this.x = 3;
35552                 this.y = 1;
35553                 break;
35554                         
35555             default :
35556                 break;
35557         }
35558         
35559         if(Roo.isTouch){
35560             this.el.on('touchstart', this.onTouchStart, this);
35561             this.el.on('touchmove', this.onTouchMove, this);
35562             this.el.on('touchend', this.onTouchEnd, this);
35563             this.el.on('contextmenu', this.onContextMenu, this);
35564         } else {
35565             this.el.on('mouseenter'  ,this.enter, this);
35566             this.el.on('mouseleave', this.leave, this);
35567             this.el.on('click', this.onClick, this);
35568         }
35569         
35570         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35571             this.parent().bricks.push(this);   
35572         }
35573         
35574     },
35575     
35576     onClick: function(e, el)
35577     {
35578         var time = this.endTimer - this.startTimer;
35579         // Roo.log(e.preventDefault());
35580         if(Roo.isTouch){
35581             if(time > 1000){
35582                 e.preventDefault();
35583                 return;
35584             }
35585         }
35586         
35587         if(!this.preventDefault){
35588             return;
35589         }
35590         
35591         e.preventDefault();
35592         
35593         if (this.activeClass != '') {
35594             this.selectBrick();
35595         }
35596         
35597         this.fireEvent('click', this, e);
35598     },
35599     
35600     enter: function(e, el)
35601     {
35602         e.preventDefault();
35603         
35604         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35605             return;
35606         }
35607         
35608         if(this.bgimage.length && this.html.length){
35609             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35610         }
35611     },
35612     
35613     leave: function(e, el)
35614     {
35615         e.preventDefault();
35616         
35617         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35618             return;
35619         }
35620         
35621         if(this.bgimage.length && this.html.length){
35622             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35623         }
35624     },
35625     
35626     onTouchStart: function(e, el)
35627     {
35628 //        e.preventDefault();
35629         
35630         this.touchmoved = false;
35631         
35632         if(!this.isFitContainer){
35633             return;
35634         }
35635         
35636         if(!this.bgimage.length || !this.html.length){
35637             return;
35638         }
35639         
35640         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35641         
35642         this.timer = new Date().getTime();
35643         
35644     },
35645     
35646     onTouchMove: function(e, el)
35647     {
35648         this.touchmoved = true;
35649     },
35650     
35651     onContextMenu : function(e,el)
35652     {
35653         e.preventDefault();
35654         e.stopPropagation();
35655         return false;
35656     },
35657     
35658     onTouchEnd: function(e, el)
35659     {
35660 //        e.preventDefault();
35661         
35662         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35663         
35664             this.leave(e,el);
35665             
35666             return;
35667         }
35668         
35669         if(!this.bgimage.length || !this.html.length){
35670             
35671             if(this.href.length){
35672                 window.location.href = this.href;
35673             }
35674             
35675             return;
35676         }
35677         
35678         if(!this.isFitContainer){
35679             return;
35680         }
35681         
35682         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35683         
35684         window.location.href = this.href;
35685     },
35686     
35687     //selection on single brick only
35688     selectBrick : function() {
35689         
35690         if (!this.parentId) {
35691             return;
35692         }
35693         
35694         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35695         var index = m.selectedBrick.indexOf(this.id);
35696         
35697         if ( index > -1) {
35698             m.selectedBrick.splice(index,1);
35699             this.el.removeClass(this.activeClass);
35700             return;
35701         }
35702         
35703         for(var i = 0; i < m.selectedBrick.length; i++) {
35704             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35705             b.el.removeClass(b.activeClass);
35706         }
35707         
35708         m.selectedBrick = [];
35709         
35710         m.selectedBrick.push(this.id);
35711         this.el.addClass(this.activeClass);
35712         return;
35713     },
35714     
35715     isSelected : function(){
35716         return this.el.hasClass(this.activeClass);
35717         
35718     }
35719 });
35720
35721 Roo.apply(Roo.bootstrap.MasonryBrick, {
35722     
35723     //groups: {},
35724     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35725      /**
35726     * register a Masonry Brick
35727     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35728     */
35729     
35730     register : function(brick)
35731     {
35732         //this.groups[brick.id] = brick;
35733         this.groups.add(brick.id, brick);
35734     },
35735     /**
35736     * fetch a  masonry brick based on the masonry brick ID
35737     * @param {string} the masonry brick to add
35738     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35739     */
35740     
35741     get: function(brick_id) 
35742     {
35743         // if (typeof(this.groups[brick_id]) == 'undefined') {
35744         //     return false;
35745         // }
35746         // return this.groups[brick_id] ;
35747         
35748         if(this.groups.key(brick_id)) {
35749             return this.groups.key(brick_id);
35750         }
35751         
35752         return false;
35753     }
35754     
35755     
35756     
35757 });
35758
35759  /*
35760  * - LGPL
35761  *
35762  * element
35763  * 
35764  */
35765
35766 /**
35767  * @class Roo.bootstrap.Brick
35768  * @extends Roo.bootstrap.Component
35769  * Bootstrap Brick class
35770  * 
35771  * @constructor
35772  * Create a new Brick
35773  * @param {Object} config The config object
35774  */
35775
35776 Roo.bootstrap.Brick = function(config){
35777     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35778     
35779     this.addEvents({
35780         // raw events
35781         /**
35782          * @event click
35783          * When a Brick is click
35784          * @param {Roo.bootstrap.Brick} this
35785          * @param {Roo.EventObject} e
35786          */
35787         "click" : true
35788     });
35789 };
35790
35791 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35792     
35793     /**
35794      * @cfg {String} title
35795      */   
35796     title : '',
35797     /**
35798      * @cfg {String} html
35799      */   
35800     html : '',
35801     /**
35802      * @cfg {String} bgimage
35803      */   
35804     bgimage : '',
35805     /**
35806      * @cfg {String} cls
35807      */   
35808     cls : '',
35809     /**
35810      * @cfg {String} href
35811      */   
35812     href : '',
35813     /**
35814      * @cfg {String} video
35815      */   
35816     video : '',
35817     /**
35818      * @cfg {Boolean} square
35819      */   
35820     square : true,
35821     
35822     getAutoCreate : function()
35823     {
35824         var cls = 'roo-brick';
35825         
35826         if(this.href.length){
35827             cls += ' roo-brick-link';
35828         }
35829         
35830         if(this.bgimage.length){
35831             cls += ' roo-brick-image';
35832         }
35833         
35834         if(!this.html.length && !this.bgimage.length){
35835             cls += ' roo-brick-center-title';
35836         }
35837         
35838         if(!this.html.length && this.bgimage.length){
35839             cls += ' roo-brick-bottom-title';
35840         }
35841         
35842         if(this.cls){
35843             cls += ' ' + this.cls;
35844         }
35845         
35846         var cfg = {
35847             tag: (this.href.length) ? 'a' : 'div',
35848             cls: cls,
35849             cn: [
35850                 {
35851                     tag: 'div',
35852                     cls: 'roo-brick-paragraph',
35853                     cn: []
35854                 }
35855             ]
35856         };
35857         
35858         if(this.href.length){
35859             cfg.href = this.href;
35860         }
35861         
35862         var cn = cfg.cn[0].cn;
35863         
35864         if(this.title.length){
35865             cn.push({
35866                 tag: 'h4',
35867                 cls: 'roo-brick-title',
35868                 html: this.title
35869             });
35870         }
35871         
35872         if(this.html.length){
35873             cn.push({
35874                 tag: 'p',
35875                 cls: 'roo-brick-text',
35876                 html: this.html
35877             });
35878         } else {
35879             cn.cls += ' hide';
35880         }
35881         
35882         if(this.bgimage.length){
35883             cfg.cn.push({
35884                 tag: 'img',
35885                 cls: 'roo-brick-image-view',
35886                 src: this.bgimage
35887             });
35888         }
35889         
35890         return cfg;
35891     },
35892     
35893     initEvents: function() 
35894     {
35895         if(this.title.length || this.html.length){
35896             this.el.on('mouseenter'  ,this.enter, this);
35897             this.el.on('mouseleave', this.leave, this);
35898         }
35899         
35900         Roo.EventManager.onWindowResize(this.resize, this); 
35901         
35902         if(this.bgimage.length){
35903             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35904             this.imageEl.on('load', this.onImageLoad, this);
35905             return;
35906         }
35907         
35908         this.resize();
35909     },
35910     
35911     onImageLoad : function()
35912     {
35913         this.resize();
35914     },
35915     
35916     resize : function()
35917     {
35918         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35919         
35920         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35921         
35922         if(this.bgimage.length){
35923             var image = this.el.select('.roo-brick-image-view', true).first();
35924             
35925             image.setWidth(paragraph.getWidth());
35926             
35927             if(this.square){
35928                 image.setHeight(paragraph.getWidth());
35929             }
35930             
35931             this.el.setHeight(image.getHeight());
35932             paragraph.setHeight(image.getHeight());
35933             
35934         }
35935         
35936     },
35937     
35938     enter: function(e, el)
35939     {
35940         e.preventDefault();
35941         
35942         if(this.bgimage.length){
35943             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35944             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35945         }
35946     },
35947     
35948     leave: function(e, el)
35949     {
35950         e.preventDefault();
35951         
35952         if(this.bgimage.length){
35953             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35954             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35955         }
35956     }
35957     
35958 });
35959
35960  
35961
35962  /*
35963  * - LGPL
35964  *
35965  * Number field 
35966  */
35967
35968 /**
35969  * @class Roo.bootstrap.NumberField
35970  * @extends Roo.bootstrap.Input
35971  * Bootstrap NumberField class
35972  * 
35973  * 
35974  * 
35975  * 
35976  * @constructor
35977  * Create a new NumberField
35978  * @param {Object} config The config object
35979  */
35980
35981 Roo.bootstrap.NumberField = function(config){
35982     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35983 };
35984
35985 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35986     
35987     /**
35988      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35989      */
35990     allowDecimals : true,
35991     /**
35992      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35993      */
35994     decimalSeparator : ".",
35995     /**
35996      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35997      */
35998     decimalPrecision : 2,
35999     /**
36000      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36001      */
36002     allowNegative : true,
36003     
36004     /**
36005      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36006      */
36007     allowZero: true,
36008     /**
36009      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36010      */
36011     minValue : Number.NEGATIVE_INFINITY,
36012     /**
36013      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36014      */
36015     maxValue : Number.MAX_VALUE,
36016     /**
36017      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36018      */
36019     minText : "The minimum value for this field is {0}",
36020     /**
36021      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36022      */
36023     maxText : "The maximum value for this field is {0}",
36024     /**
36025      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36026      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36027      */
36028     nanText : "{0} is not a valid number",
36029     /**
36030      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36031      */
36032     thousandsDelimiter : false,
36033     /**
36034      * @cfg {String} valueAlign alignment of value
36035      */
36036     valueAlign : "left",
36037
36038     getAutoCreate : function()
36039     {
36040         var hiddenInput = {
36041             tag: 'input',
36042             type: 'hidden',
36043             id: Roo.id(),
36044             cls: 'hidden-number-input'
36045         };
36046         
36047         if (this.name) {
36048             hiddenInput.name = this.name;
36049         }
36050         
36051         this.name = '';
36052         
36053         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36054         
36055         this.name = hiddenInput.name;
36056         
36057         if(cfg.cn.length > 0) {
36058             cfg.cn.push(hiddenInput);
36059         }
36060         
36061         return cfg;
36062     },
36063
36064     // private
36065     initEvents : function()
36066     {   
36067         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36068         
36069         var allowed = "0123456789";
36070         
36071         if(this.allowDecimals){
36072             allowed += this.decimalSeparator;
36073         }
36074         
36075         if(this.allowNegative){
36076             allowed += "-";
36077         }
36078         
36079         if(this.thousandsDelimiter) {
36080             allowed += ",";
36081         }
36082         
36083         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36084         
36085         var keyPress = function(e){
36086             
36087             var k = e.getKey();
36088             
36089             var c = e.getCharCode();
36090             
36091             if(
36092                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36093                     allowed.indexOf(String.fromCharCode(c)) === -1
36094             ){
36095                 e.stopEvent();
36096                 return;
36097             }
36098             
36099             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36100                 return;
36101             }
36102             
36103             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36104                 e.stopEvent();
36105             }
36106         };
36107         
36108         this.el.on("keypress", keyPress, this);
36109     },
36110     
36111     validateValue : function(value)
36112     {
36113         
36114         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36115             return false;
36116         }
36117         
36118         var num = this.parseValue(value);
36119         
36120         if(isNaN(num)){
36121             this.markInvalid(String.format(this.nanText, value));
36122             return false;
36123         }
36124         
36125         if(num < this.minValue){
36126             this.markInvalid(String.format(this.minText, this.minValue));
36127             return false;
36128         }
36129         
36130         if(num > this.maxValue){
36131             this.markInvalid(String.format(this.maxText, this.maxValue));
36132             return false;
36133         }
36134         
36135         return true;
36136     },
36137
36138     getValue : function()
36139     {
36140         var v = this.hiddenEl().getValue();
36141         
36142         return this.fixPrecision(this.parseValue(v));
36143     },
36144
36145     parseValue : function(value)
36146     {
36147         if(this.thousandsDelimiter) {
36148             value += "";
36149             r = new RegExp(",", "g");
36150             value = value.replace(r, "");
36151         }
36152         
36153         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36154         return isNaN(value) ? '' : value;
36155     },
36156
36157     fixPrecision : function(value)
36158     {
36159         if(this.thousandsDelimiter) {
36160             value += "";
36161             r = new RegExp(",", "g");
36162             value = value.replace(r, "");
36163         }
36164         
36165         var nan = isNaN(value);
36166         
36167         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36168             return nan ? '' : value;
36169         }
36170         return parseFloat(value).toFixed(this.decimalPrecision);
36171     },
36172
36173     setValue : function(v)
36174     {
36175         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36176         
36177         this.value = v;
36178         
36179         if(this.rendered){
36180             
36181             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36182             
36183             this.inputEl().dom.value = (v == '') ? '' :
36184                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36185             
36186             if(!this.allowZero && v === '0') {
36187                 this.hiddenEl().dom.value = '';
36188                 this.inputEl().dom.value = '';
36189             }
36190             
36191             this.validate();
36192         }
36193     },
36194
36195     decimalPrecisionFcn : function(v)
36196     {
36197         return Math.floor(v);
36198     },
36199
36200     beforeBlur : function()
36201     {
36202         var v = this.parseValue(this.getRawValue());
36203         
36204         if(v || v === 0 || v === ''){
36205             this.setValue(v);
36206         }
36207     },
36208     
36209     hiddenEl : function()
36210     {
36211         return this.el.select('input.hidden-number-input',true).first();
36212     }
36213     
36214 });
36215
36216  
36217
36218 /*
36219 * Licence: LGPL
36220 */
36221
36222 /**
36223  * @class Roo.bootstrap.DocumentSlider
36224  * @extends Roo.bootstrap.Component
36225  * Bootstrap DocumentSlider class
36226  * 
36227  * @constructor
36228  * Create a new DocumentViewer
36229  * @param {Object} config The config object
36230  */
36231
36232 Roo.bootstrap.DocumentSlider = function(config){
36233     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36234     
36235     this.files = [];
36236     
36237     this.addEvents({
36238         /**
36239          * @event initial
36240          * Fire after initEvent
36241          * @param {Roo.bootstrap.DocumentSlider} this
36242          */
36243         "initial" : true,
36244         /**
36245          * @event update
36246          * Fire after update
36247          * @param {Roo.bootstrap.DocumentSlider} this
36248          */
36249         "update" : true,
36250         /**
36251          * @event click
36252          * Fire after click
36253          * @param {Roo.bootstrap.DocumentSlider} this
36254          */
36255         "click" : true
36256     });
36257 };
36258
36259 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36260     
36261     files : false,
36262     
36263     indicator : 0,
36264     
36265     getAutoCreate : function()
36266     {
36267         var cfg = {
36268             tag : 'div',
36269             cls : 'roo-document-slider',
36270             cn : [
36271                 {
36272                     tag : 'div',
36273                     cls : 'roo-document-slider-header',
36274                     cn : [
36275                         {
36276                             tag : 'div',
36277                             cls : 'roo-document-slider-header-title'
36278                         }
36279                     ]
36280                 },
36281                 {
36282                     tag : 'div',
36283                     cls : 'roo-document-slider-body',
36284                     cn : [
36285                         {
36286                             tag : 'div',
36287                             cls : 'roo-document-slider-prev',
36288                             cn : [
36289                                 {
36290                                     tag : 'i',
36291                                     cls : 'fa fa-chevron-left'
36292                                 }
36293                             ]
36294                         },
36295                         {
36296                             tag : 'div',
36297                             cls : 'roo-document-slider-thumb',
36298                             cn : [
36299                                 {
36300                                     tag : 'img',
36301                                     cls : 'roo-document-slider-image'
36302                                 }
36303                             ]
36304                         },
36305                         {
36306                             tag : 'div',
36307                             cls : 'roo-document-slider-next',
36308                             cn : [
36309                                 {
36310                                     tag : 'i',
36311                                     cls : 'fa fa-chevron-right'
36312                                 }
36313                             ]
36314                         }
36315                     ]
36316                 }
36317             ]
36318         };
36319         
36320         return cfg;
36321     },
36322     
36323     initEvents : function()
36324     {
36325         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36326         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36327         
36328         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36329         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36330         
36331         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36332         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36333         
36334         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36335         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36336         
36337         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36338         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36339         
36340         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36341         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36342         
36343         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36344         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36345         
36346         this.thumbEl.on('click', this.onClick, this);
36347         
36348         this.prevIndicator.on('click', this.prev, this);
36349         
36350         this.nextIndicator.on('click', this.next, this);
36351         
36352     },
36353     
36354     initial : function()
36355     {
36356         if(this.files.length){
36357             this.indicator = 1;
36358             this.update()
36359         }
36360         
36361         this.fireEvent('initial', this);
36362     },
36363     
36364     update : function()
36365     {
36366         this.imageEl.attr('src', this.files[this.indicator - 1]);
36367         
36368         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36369         
36370         this.prevIndicator.show();
36371         
36372         if(this.indicator == 1){
36373             this.prevIndicator.hide();
36374         }
36375         
36376         this.nextIndicator.show();
36377         
36378         if(this.indicator == this.files.length){
36379             this.nextIndicator.hide();
36380         }
36381         
36382         this.thumbEl.scrollTo('top');
36383         
36384         this.fireEvent('update', this);
36385     },
36386     
36387     onClick : function(e)
36388     {
36389         e.preventDefault();
36390         
36391         this.fireEvent('click', this);
36392     },
36393     
36394     prev : function(e)
36395     {
36396         e.preventDefault();
36397         
36398         this.indicator = Math.max(1, this.indicator - 1);
36399         
36400         this.update();
36401     },
36402     
36403     next : function(e)
36404     {
36405         e.preventDefault();
36406         
36407         this.indicator = Math.min(this.files.length, this.indicator + 1);
36408         
36409         this.update();
36410     }
36411 });
36412 /*
36413  * - LGPL
36414  *
36415  * RadioSet
36416  *
36417  *
36418  */
36419
36420 /**
36421  * @class Roo.bootstrap.RadioSet
36422  * @extends Roo.bootstrap.Input
36423  * Bootstrap RadioSet class
36424  * @cfg {String} indicatorpos (left|right) default left
36425  * @cfg {Boolean} inline (true|false) inline the element (default true)
36426  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36427  * @constructor
36428  * Create a new RadioSet
36429  * @param {Object} config The config object
36430  */
36431
36432 Roo.bootstrap.RadioSet = function(config){
36433     
36434     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36435     
36436     this.radioes = [];
36437     
36438     Roo.bootstrap.RadioSet.register(this);
36439     
36440     this.addEvents({
36441         /**
36442         * @event check
36443         * Fires when the element is checked or unchecked.
36444         * @param {Roo.bootstrap.RadioSet} this This radio
36445         * @param {Roo.bootstrap.Radio} item The checked item
36446         */
36447        check : true,
36448        /**
36449         * @event click
36450         * Fires when the element is click.
36451         * @param {Roo.bootstrap.RadioSet} this This radio set
36452         * @param {Roo.bootstrap.Radio} item The checked item
36453         * @param {Roo.EventObject} e The event object
36454         */
36455        click : true
36456     });
36457     
36458 };
36459
36460 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36461
36462     radioes : false,
36463     
36464     inline : true,
36465     
36466     weight : '',
36467     
36468     indicatorpos : 'left',
36469     
36470     getAutoCreate : function()
36471     {
36472         var label = {
36473             tag : 'label',
36474             cls : 'roo-radio-set-label',
36475             cn : [
36476                 {
36477                     tag : 'span',
36478                     html : this.fieldLabel
36479                 }
36480             ]
36481         };
36482         if (Roo.bootstrap.version == 3) {
36483             
36484             
36485             if(this.indicatorpos == 'left'){
36486                 label.cn.unshift({
36487                     tag : 'i',
36488                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36489                     tooltip : 'This field is required'
36490                 });
36491             } else {
36492                 label.cn.push({
36493                     tag : 'i',
36494                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36495                     tooltip : 'This field is required'
36496                 });
36497             }
36498         }
36499         var items = {
36500             tag : 'div',
36501             cls : 'roo-radio-set-items'
36502         };
36503         
36504         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36505         
36506         if (align === 'left' && this.fieldLabel.length) {
36507             
36508             items = {
36509                 cls : "roo-radio-set-right", 
36510                 cn: [
36511                     items
36512                 ]
36513             };
36514             
36515             if(this.labelWidth > 12){
36516                 label.style = "width: " + this.labelWidth + 'px';
36517             }
36518             
36519             if(this.labelWidth < 13 && this.labelmd == 0){
36520                 this.labelmd = this.labelWidth;
36521             }
36522             
36523             if(this.labellg > 0){
36524                 label.cls += ' col-lg-' + this.labellg;
36525                 items.cls += ' col-lg-' + (12 - this.labellg);
36526             }
36527             
36528             if(this.labelmd > 0){
36529                 label.cls += ' col-md-' + this.labelmd;
36530                 items.cls += ' col-md-' + (12 - this.labelmd);
36531             }
36532             
36533             if(this.labelsm > 0){
36534                 label.cls += ' col-sm-' + this.labelsm;
36535                 items.cls += ' col-sm-' + (12 - this.labelsm);
36536             }
36537             
36538             if(this.labelxs > 0){
36539                 label.cls += ' col-xs-' + this.labelxs;
36540                 items.cls += ' col-xs-' + (12 - this.labelxs);
36541             }
36542         }
36543         
36544         var cfg = {
36545             tag : 'div',
36546             cls : 'roo-radio-set',
36547             cn : [
36548                 {
36549                     tag : 'input',
36550                     cls : 'roo-radio-set-input',
36551                     type : 'hidden',
36552                     name : this.name,
36553                     value : this.value ? this.value :  ''
36554                 },
36555                 label,
36556                 items
36557             ]
36558         };
36559         
36560         if(this.weight.length){
36561             cfg.cls += ' roo-radio-' + this.weight;
36562         }
36563         
36564         if(this.inline) {
36565             cfg.cls += ' roo-radio-set-inline';
36566         }
36567         
36568         var settings=this;
36569         ['xs','sm','md','lg'].map(function(size){
36570             if (settings[size]) {
36571                 cfg.cls += ' col-' + size + '-' + settings[size];
36572             }
36573         });
36574         
36575         return cfg;
36576         
36577     },
36578
36579     initEvents : function()
36580     {
36581         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36582         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36583         
36584         if(!this.fieldLabel.length){
36585             this.labelEl.hide();
36586         }
36587         
36588         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36589         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36590         
36591         this.indicator = this.indicatorEl();
36592         
36593         if(this.indicator){
36594             this.indicator.addClass('invisible');
36595         }
36596         
36597         this.originalValue = this.getValue();
36598         
36599     },
36600     
36601     inputEl: function ()
36602     {
36603         return this.el.select('.roo-radio-set-input', true).first();
36604     },
36605     
36606     getChildContainer : function()
36607     {
36608         return this.itemsEl;
36609     },
36610     
36611     register : function(item)
36612     {
36613         this.radioes.push(item);
36614         
36615     },
36616     
36617     validate : function()
36618     {   
36619         if(this.getVisibilityEl().hasClass('hidden')){
36620             return true;
36621         }
36622         
36623         var valid = false;
36624         
36625         Roo.each(this.radioes, function(i){
36626             if(!i.checked){
36627                 return;
36628             }
36629             
36630             valid = true;
36631             return false;
36632         });
36633         
36634         if(this.allowBlank) {
36635             return true;
36636         }
36637         
36638         if(this.disabled || valid){
36639             this.markValid();
36640             return true;
36641         }
36642         
36643         this.markInvalid();
36644         return false;
36645         
36646     },
36647     
36648     markValid : function()
36649     {
36650         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36651             this.indicatorEl().removeClass('visible');
36652             this.indicatorEl().addClass('invisible');
36653         }
36654         
36655         
36656         if (Roo.bootstrap.version == 3) {
36657             this.el.removeClass([this.invalidClass, this.validClass]);
36658             this.el.addClass(this.validClass);
36659         } else {
36660             this.el.removeClass(['is-invalid','is-valid']);
36661             this.el.addClass(['is-valid']);
36662         }
36663         this.fireEvent('valid', this);
36664     },
36665     
36666     markInvalid : function(msg)
36667     {
36668         if(this.allowBlank || this.disabled){
36669             return;
36670         }
36671         
36672         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36673             this.indicatorEl().removeClass('invisible');
36674             this.indicatorEl().addClass('visible');
36675         }
36676         if (Roo.bootstrap.version == 3) {
36677             this.el.removeClass([this.invalidClass, this.validClass]);
36678             this.el.addClass(this.invalidClass);
36679         } else {
36680             this.el.removeClass(['is-invalid','is-valid']);
36681             this.el.addClass(['is-invalid']);
36682         }
36683         
36684         this.fireEvent('invalid', this, msg);
36685         
36686     },
36687     
36688     setValue : function(v, suppressEvent)
36689     {   
36690         if(this.value === v){
36691             return;
36692         }
36693         
36694         this.value = v;
36695         
36696         if(this.rendered){
36697             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36698         }
36699         
36700         Roo.each(this.radioes, function(i){
36701             i.checked = false;
36702             i.el.removeClass('checked');
36703         });
36704         
36705         Roo.each(this.radioes, function(i){
36706             
36707             if(i.value === v || i.value.toString() === v.toString()){
36708                 i.checked = true;
36709                 i.el.addClass('checked');
36710                 
36711                 if(suppressEvent !== true){
36712                     this.fireEvent('check', this, i);
36713                 }
36714                 
36715                 return false;
36716             }
36717             
36718         }, this);
36719         
36720         this.validate();
36721     },
36722     
36723     clearInvalid : function(){
36724         
36725         if(!this.el || this.preventMark){
36726             return;
36727         }
36728         
36729         this.el.removeClass([this.invalidClass]);
36730         
36731         this.fireEvent('valid', this);
36732     }
36733     
36734 });
36735
36736 Roo.apply(Roo.bootstrap.RadioSet, {
36737     
36738     groups: {},
36739     
36740     register : function(set)
36741     {
36742         this.groups[set.name] = set;
36743     },
36744     
36745     get: function(name) 
36746     {
36747         if (typeof(this.groups[name]) == 'undefined') {
36748             return false;
36749         }
36750         
36751         return this.groups[name] ;
36752     }
36753     
36754 });
36755 /*
36756  * Based on:
36757  * Ext JS Library 1.1.1
36758  * Copyright(c) 2006-2007, Ext JS, LLC.
36759  *
36760  * Originally Released Under LGPL - original licence link has changed is not relivant.
36761  *
36762  * Fork - LGPL
36763  * <script type="text/javascript">
36764  */
36765
36766
36767 /**
36768  * @class Roo.bootstrap.SplitBar
36769  * @extends Roo.util.Observable
36770  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36771  * <br><br>
36772  * Usage:
36773  * <pre><code>
36774 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36775                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36776 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36777 split.minSize = 100;
36778 split.maxSize = 600;
36779 split.animate = true;
36780 split.on('moved', splitterMoved);
36781 </code></pre>
36782  * @constructor
36783  * Create a new SplitBar
36784  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36785  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36786  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36787  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36788                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36789                         position of the SplitBar).
36790  */
36791 Roo.bootstrap.SplitBar = function(cfg){
36792     
36793     /** @private */
36794     
36795     //{
36796     //  dragElement : elm
36797     //  resizingElement: el,
36798         // optional..
36799     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36800     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36801         // existingProxy ???
36802     //}
36803     
36804     this.el = Roo.get(cfg.dragElement, true);
36805     this.el.dom.unselectable = "on";
36806     /** @private */
36807     this.resizingEl = Roo.get(cfg.resizingElement, true);
36808
36809     /**
36810      * @private
36811      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36812      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36813      * @type Number
36814      */
36815     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36816     
36817     /**
36818      * The minimum size of the resizing element. (Defaults to 0)
36819      * @type Number
36820      */
36821     this.minSize = 0;
36822     
36823     /**
36824      * The maximum size of the resizing element. (Defaults to 2000)
36825      * @type Number
36826      */
36827     this.maxSize = 2000;
36828     
36829     /**
36830      * Whether to animate the transition to the new size
36831      * @type Boolean
36832      */
36833     this.animate = false;
36834     
36835     /**
36836      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36837      * @type Boolean
36838      */
36839     this.useShim = false;
36840     
36841     /** @private */
36842     this.shim = null;
36843     
36844     if(!cfg.existingProxy){
36845         /** @private */
36846         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36847     }else{
36848         this.proxy = Roo.get(cfg.existingProxy).dom;
36849     }
36850     /** @private */
36851     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36852     
36853     /** @private */
36854     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36855     
36856     /** @private */
36857     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36858     
36859     /** @private */
36860     this.dragSpecs = {};
36861     
36862     /**
36863      * @private The adapter to use to positon and resize elements
36864      */
36865     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36866     this.adapter.init(this);
36867     
36868     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36869         /** @private */
36870         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36871         this.el.addClass("roo-splitbar-h");
36872     }else{
36873         /** @private */
36874         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36875         this.el.addClass("roo-splitbar-v");
36876     }
36877     
36878     this.addEvents({
36879         /**
36880          * @event resize
36881          * Fires when the splitter is moved (alias for {@link #event-moved})
36882          * @param {Roo.bootstrap.SplitBar} this
36883          * @param {Number} newSize the new width or height
36884          */
36885         "resize" : true,
36886         /**
36887          * @event moved
36888          * Fires when the splitter is moved
36889          * @param {Roo.bootstrap.SplitBar} this
36890          * @param {Number} newSize the new width or height
36891          */
36892         "moved" : true,
36893         /**
36894          * @event beforeresize
36895          * Fires before the splitter is dragged
36896          * @param {Roo.bootstrap.SplitBar} this
36897          */
36898         "beforeresize" : true,
36899
36900         "beforeapply" : true
36901     });
36902
36903     Roo.util.Observable.call(this);
36904 };
36905
36906 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36907     onStartProxyDrag : function(x, y){
36908         this.fireEvent("beforeresize", this);
36909         if(!this.overlay){
36910             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36911             o.unselectable();
36912             o.enableDisplayMode("block");
36913             // all splitbars share the same overlay
36914             Roo.bootstrap.SplitBar.prototype.overlay = o;
36915         }
36916         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36917         this.overlay.show();
36918         Roo.get(this.proxy).setDisplayed("block");
36919         var size = this.adapter.getElementSize(this);
36920         this.activeMinSize = this.getMinimumSize();;
36921         this.activeMaxSize = this.getMaximumSize();;
36922         var c1 = size - this.activeMinSize;
36923         var c2 = Math.max(this.activeMaxSize - size, 0);
36924         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36925             this.dd.resetConstraints();
36926             this.dd.setXConstraint(
36927                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36928                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36929             );
36930             this.dd.setYConstraint(0, 0);
36931         }else{
36932             this.dd.resetConstraints();
36933             this.dd.setXConstraint(0, 0);
36934             this.dd.setYConstraint(
36935                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36936                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36937             );
36938          }
36939         this.dragSpecs.startSize = size;
36940         this.dragSpecs.startPoint = [x, y];
36941         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36942     },
36943     
36944     /** 
36945      * @private Called after the drag operation by the DDProxy
36946      */
36947     onEndProxyDrag : function(e){
36948         Roo.get(this.proxy).setDisplayed(false);
36949         var endPoint = Roo.lib.Event.getXY(e);
36950         if(this.overlay){
36951             this.overlay.hide();
36952         }
36953         var newSize;
36954         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36955             newSize = this.dragSpecs.startSize + 
36956                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36957                     endPoint[0] - this.dragSpecs.startPoint[0] :
36958                     this.dragSpecs.startPoint[0] - endPoint[0]
36959                 );
36960         }else{
36961             newSize = this.dragSpecs.startSize + 
36962                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36963                     endPoint[1] - this.dragSpecs.startPoint[1] :
36964                     this.dragSpecs.startPoint[1] - endPoint[1]
36965                 );
36966         }
36967         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36968         if(newSize != this.dragSpecs.startSize){
36969             if(this.fireEvent('beforeapply', this, newSize) !== false){
36970                 this.adapter.setElementSize(this, newSize);
36971                 this.fireEvent("moved", this, newSize);
36972                 this.fireEvent("resize", this, newSize);
36973             }
36974         }
36975     },
36976     
36977     /**
36978      * Get the adapter this SplitBar uses
36979      * @return The adapter object
36980      */
36981     getAdapter : function(){
36982         return this.adapter;
36983     },
36984     
36985     /**
36986      * Set the adapter this SplitBar uses
36987      * @param {Object} adapter A SplitBar adapter object
36988      */
36989     setAdapter : function(adapter){
36990         this.adapter = adapter;
36991         this.adapter.init(this);
36992     },
36993     
36994     /**
36995      * Gets the minimum size for the resizing element
36996      * @return {Number} The minimum size
36997      */
36998     getMinimumSize : function(){
36999         return this.minSize;
37000     },
37001     
37002     /**
37003      * Sets the minimum size for the resizing element
37004      * @param {Number} minSize The minimum size
37005      */
37006     setMinimumSize : function(minSize){
37007         this.minSize = minSize;
37008     },
37009     
37010     /**
37011      * Gets the maximum size for the resizing element
37012      * @return {Number} The maximum size
37013      */
37014     getMaximumSize : function(){
37015         return this.maxSize;
37016     },
37017     
37018     /**
37019      * Sets the maximum size for the resizing element
37020      * @param {Number} maxSize The maximum size
37021      */
37022     setMaximumSize : function(maxSize){
37023         this.maxSize = maxSize;
37024     },
37025     
37026     /**
37027      * Sets the initialize size for the resizing element
37028      * @param {Number} size The initial size
37029      */
37030     setCurrentSize : function(size){
37031         var oldAnimate = this.animate;
37032         this.animate = false;
37033         this.adapter.setElementSize(this, size);
37034         this.animate = oldAnimate;
37035     },
37036     
37037     /**
37038      * Destroy this splitbar. 
37039      * @param {Boolean} removeEl True to remove the element
37040      */
37041     destroy : function(removeEl){
37042         if(this.shim){
37043             this.shim.remove();
37044         }
37045         this.dd.unreg();
37046         this.proxy.parentNode.removeChild(this.proxy);
37047         if(removeEl){
37048             this.el.remove();
37049         }
37050     }
37051 });
37052
37053 /**
37054  * @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.
37055  */
37056 Roo.bootstrap.SplitBar.createProxy = function(dir){
37057     var proxy = new Roo.Element(document.createElement("div"));
37058     proxy.unselectable();
37059     var cls = 'roo-splitbar-proxy';
37060     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37061     document.body.appendChild(proxy.dom);
37062     return proxy.dom;
37063 };
37064
37065 /** 
37066  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37067  * Default Adapter. It assumes the splitter and resizing element are not positioned
37068  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37069  */
37070 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37071 };
37072
37073 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37074     // do nothing for now
37075     init : function(s){
37076     
37077     },
37078     /**
37079      * Called before drag operations to get the current size of the resizing element. 
37080      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37081      */
37082      getElementSize : function(s){
37083         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37084             return s.resizingEl.getWidth();
37085         }else{
37086             return s.resizingEl.getHeight();
37087         }
37088     },
37089     
37090     /**
37091      * Called after drag operations to set the size of the resizing element.
37092      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37093      * @param {Number} newSize The new size to set
37094      * @param {Function} onComplete A function to be invoked when resizing is complete
37095      */
37096     setElementSize : function(s, newSize, onComplete){
37097         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37098             if(!s.animate){
37099                 s.resizingEl.setWidth(newSize);
37100                 if(onComplete){
37101                     onComplete(s, newSize);
37102                 }
37103             }else{
37104                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37105             }
37106         }else{
37107             
37108             if(!s.animate){
37109                 s.resizingEl.setHeight(newSize);
37110                 if(onComplete){
37111                     onComplete(s, newSize);
37112                 }
37113             }else{
37114                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37115             }
37116         }
37117     }
37118 };
37119
37120 /** 
37121  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37122  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37123  * Adapter that  moves the splitter element to align with the resized sizing element. 
37124  * Used with an absolute positioned SplitBar.
37125  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37126  * document.body, make sure you assign an id to the body element.
37127  */
37128 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37129     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37130     this.container = Roo.get(container);
37131 };
37132
37133 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37134     init : function(s){
37135         this.basic.init(s);
37136     },
37137     
37138     getElementSize : function(s){
37139         return this.basic.getElementSize(s);
37140     },
37141     
37142     setElementSize : function(s, newSize, onComplete){
37143         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37144     },
37145     
37146     moveSplitter : function(s){
37147         var yes = Roo.bootstrap.SplitBar;
37148         switch(s.placement){
37149             case yes.LEFT:
37150                 s.el.setX(s.resizingEl.getRight());
37151                 break;
37152             case yes.RIGHT:
37153                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37154                 break;
37155             case yes.TOP:
37156                 s.el.setY(s.resizingEl.getBottom());
37157                 break;
37158             case yes.BOTTOM:
37159                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37160                 break;
37161         }
37162     }
37163 };
37164
37165 /**
37166  * Orientation constant - Create a vertical SplitBar
37167  * @static
37168  * @type Number
37169  */
37170 Roo.bootstrap.SplitBar.VERTICAL = 1;
37171
37172 /**
37173  * Orientation constant - Create a horizontal SplitBar
37174  * @static
37175  * @type Number
37176  */
37177 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37178
37179 /**
37180  * Placement constant - The resizing element is to the left of the splitter element
37181  * @static
37182  * @type Number
37183  */
37184 Roo.bootstrap.SplitBar.LEFT = 1;
37185
37186 /**
37187  * Placement constant - The resizing element is to the right of the splitter element
37188  * @static
37189  * @type Number
37190  */
37191 Roo.bootstrap.SplitBar.RIGHT = 2;
37192
37193 /**
37194  * Placement constant - The resizing element is positioned above the splitter element
37195  * @static
37196  * @type Number
37197  */
37198 Roo.bootstrap.SplitBar.TOP = 3;
37199
37200 /**
37201  * Placement constant - The resizing element is positioned under splitter element
37202  * @static
37203  * @type Number
37204  */
37205 Roo.bootstrap.SplitBar.BOTTOM = 4;
37206 Roo.namespace("Roo.bootstrap.layout");/*
37207  * Based on:
37208  * Ext JS Library 1.1.1
37209  * Copyright(c) 2006-2007, Ext JS, LLC.
37210  *
37211  * Originally Released Under LGPL - original licence link has changed is not relivant.
37212  *
37213  * Fork - LGPL
37214  * <script type="text/javascript">
37215  */
37216
37217 /**
37218  * @class Roo.bootstrap.layout.Manager
37219  * @extends Roo.bootstrap.Component
37220  * Base class for layout managers.
37221  */
37222 Roo.bootstrap.layout.Manager = function(config)
37223 {
37224     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37225
37226
37227
37228
37229
37230     /** false to disable window resize monitoring @type Boolean */
37231     this.monitorWindowResize = true;
37232     this.regions = {};
37233     this.addEvents({
37234         /**
37235          * @event layout
37236          * Fires when a layout is performed.
37237          * @param {Roo.LayoutManager} this
37238          */
37239         "layout" : true,
37240         /**
37241          * @event regionresized
37242          * Fires when the user resizes a region.
37243          * @param {Roo.LayoutRegion} region The resized region
37244          * @param {Number} newSize The new size (width for east/west, height for north/south)
37245          */
37246         "regionresized" : true,
37247         /**
37248          * @event regioncollapsed
37249          * Fires when a region is collapsed.
37250          * @param {Roo.LayoutRegion} region The collapsed region
37251          */
37252         "regioncollapsed" : true,
37253         /**
37254          * @event regionexpanded
37255          * Fires when a region is expanded.
37256          * @param {Roo.LayoutRegion} region The expanded region
37257          */
37258         "regionexpanded" : true
37259     });
37260     this.updating = false;
37261
37262     if (config.el) {
37263         this.el = Roo.get(config.el);
37264         this.initEvents();
37265     }
37266
37267 };
37268
37269 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37270
37271
37272     regions : null,
37273
37274     monitorWindowResize : true,
37275
37276
37277     updating : false,
37278
37279
37280     onRender : function(ct, position)
37281     {
37282         if(!this.el){
37283             this.el = Roo.get(ct);
37284             this.initEvents();
37285         }
37286         //this.fireEvent('render',this);
37287     },
37288
37289
37290     initEvents: function()
37291     {
37292
37293
37294         // ie scrollbar fix
37295         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37296             document.body.scroll = "no";
37297         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37298             this.el.position('relative');
37299         }
37300         this.id = this.el.id;
37301         this.el.addClass("roo-layout-container");
37302         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37303         if(this.el.dom != document.body ) {
37304             this.el.on('resize', this.layout,this);
37305             this.el.on('show', this.layout,this);
37306         }
37307
37308     },
37309
37310     /**
37311      * Returns true if this layout is currently being updated
37312      * @return {Boolean}
37313      */
37314     isUpdating : function(){
37315         return this.updating;
37316     },
37317
37318     /**
37319      * Suspend the LayoutManager from doing auto-layouts while
37320      * making multiple add or remove calls
37321      */
37322     beginUpdate : function(){
37323         this.updating = true;
37324     },
37325
37326     /**
37327      * Restore auto-layouts and optionally disable the manager from performing a layout
37328      * @param {Boolean} noLayout true to disable a layout update
37329      */
37330     endUpdate : function(noLayout){
37331         this.updating = false;
37332         if(!noLayout){
37333             this.layout();
37334         }
37335     },
37336
37337     layout: function(){
37338         // abstract...
37339     },
37340
37341     onRegionResized : function(region, newSize){
37342         this.fireEvent("regionresized", region, newSize);
37343         this.layout();
37344     },
37345
37346     onRegionCollapsed : function(region){
37347         this.fireEvent("regioncollapsed", region);
37348     },
37349
37350     onRegionExpanded : function(region){
37351         this.fireEvent("regionexpanded", region);
37352     },
37353
37354     /**
37355      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37356      * performs box-model adjustments.
37357      * @return {Object} The size as an object {width: (the width), height: (the height)}
37358      */
37359     getViewSize : function()
37360     {
37361         var size;
37362         if(this.el.dom != document.body){
37363             size = this.el.getSize();
37364         }else{
37365             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37366         }
37367         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37368         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37369         return size;
37370     },
37371
37372     /**
37373      * Returns the Element this layout is bound to.
37374      * @return {Roo.Element}
37375      */
37376     getEl : function(){
37377         return this.el;
37378     },
37379
37380     /**
37381      * Returns the specified region.
37382      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37383      * @return {Roo.LayoutRegion}
37384      */
37385     getRegion : function(target){
37386         return this.regions[target.toLowerCase()];
37387     },
37388
37389     onWindowResize : function(){
37390         if(this.monitorWindowResize){
37391             this.layout();
37392         }
37393     }
37394 });
37395 /*
37396  * Based on:
37397  * Ext JS Library 1.1.1
37398  * Copyright(c) 2006-2007, Ext JS, LLC.
37399  *
37400  * Originally Released Under LGPL - original licence link has changed is not relivant.
37401  *
37402  * Fork - LGPL
37403  * <script type="text/javascript">
37404  */
37405 /**
37406  * @class Roo.bootstrap.layout.Border
37407  * @extends Roo.bootstrap.layout.Manager
37408  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37409  * please see: examples/bootstrap/nested.html<br><br>
37410  
37411 <b>The container the layout is rendered into can be either the body element or any other element.
37412 If it is not the body element, the container needs to either be an absolute positioned element,
37413 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37414 the container size if it is not the body element.</b>
37415
37416 * @constructor
37417 * Create a new Border
37418 * @param {Object} config Configuration options
37419  */
37420 Roo.bootstrap.layout.Border = function(config){
37421     config = config || {};
37422     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37423     
37424     
37425     
37426     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37427         if(config[region]){
37428             config[region].region = region;
37429             this.addRegion(config[region]);
37430         }
37431     },this);
37432     
37433 };
37434
37435 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37436
37437 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37438     
37439     parent : false, // this might point to a 'nest' or a ???
37440     
37441     /**
37442      * Creates and adds a new region if it doesn't already exist.
37443      * @param {String} target The target region key (north, south, east, west or center).
37444      * @param {Object} config The regions config object
37445      * @return {BorderLayoutRegion} The new region
37446      */
37447     addRegion : function(config)
37448     {
37449         if(!this.regions[config.region]){
37450             var r = this.factory(config);
37451             this.bindRegion(r);
37452         }
37453         return this.regions[config.region];
37454     },
37455
37456     // private (kinda)
37457     bindRegion : function(r){
37458         this.regions[r.config.region] = r;
37459         
37460         r.on("visibilitychange",    this.layout, this);
37461         r.on("paneladded",          this.layout, this);
37462         r.on("panelremoved",        this.layout, this);
37463         r.on("invalidated",         this.layout, this);
37464         r.on("resized",             this.onRegionResized, this);
37465         r.on("collapsed",           this.onRegionCollapsed, this);
37466         r.on("expanded",            this.onRegionExpanded, this);
37467     },
37468
37469     /**
37470      * Performs a layout update.
37471      */
37472     layout : function()
37473     {
37474         if(this.updating) {
37475             return;
37476         }
37477         
37478         // render all the rebions if they have not been done alreayd?
37479         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37480             if(this.regions[region] && !this.regions[region].bodyEl){
37481                 this.regions[region].onRender(this.el)
37482             }
37483         },this);
37484         
37485         var size = this.getViewSize();
37486         var w = size.width;
37487         var h = size.height;
37488         var centerW = w;
37489         var centerH = h;
37490         var centerY = 0;
37491         var centerX = 0;
37492         //var x = 0, y = 0;
37493
37494         var rs = this.regions;
37495         var north = rs["north"];
37496         var south = rs["south"]; 
37497         var west = rs["west"];
37498         var east = rs["east"];
37499         var center = rs["center"];
37500         //if(this.hideOnLayout){ // not supported anymore
37501             //c.el.setStyle("display", "none");
37502         //}
37503         if(north && north.isVisible()){
37504             var b = north.getBox();
37505             var m = north.getMargins();
37506             b.width = w - (m.left+m.right);
37507             b.x = m.left;
37508             b.y = m.top;
37509             centerY = b.height + b.y + m.bottom;
37510             centerH -= centerY;
37511             north.updateBox(this.safeBox(b));
37512         }
37513         if(south && south.isVisible()){
37514             var b = south.getBox();
37515             var m = south.getMargins();
37516             b.width = w - (m.left+m.right);
37517             b.x = m.left;
37518             var totalHeight = (b.height + m.top + m.bottom);
37519             b.y = h - totalHeight + m.top;
37520             centerH -= totalHeight;
37521             south.updateBox(this.safeBox(b));
37522         }
37523         if(west && west.isVisible()){
37524             var b = west.getBox();
37525             var m = west.getMargins();
37526             b.height = centerH - (m.top+m.bottom);
37527             b.x = m.left;
37528             b.y = centerY + m.top;
37529             var totalWidth = (b.width + m.left + m.right);
37530             centerX += totalWidth;
37531             centerW -= totalWidth;
37532             west.updateBox(this.safeBox(b));
37533         }
37534         if(east && east.isVisible()){
37535             var b = east.getBox();
37536             var m = east.getMargins();
37537             b.height = centerH - (m.top+m.bottom);
37538             var totalWidth = (b.width + m.left + m.right);
37539             b.x = w - totalWidth + m.left;
37540             b.y = centerY + m.top;
37541             centerW -= totalWidth;
37542             east.updateBox(this.safeBox(b));
37543         }
37544         if(center){
37545             var m = center.getMargins();
37546             var centerBox = {
37547                 x: centerX + m.left,
37548                 y: centerY + m.top,
37549                 width: centerW - (m.left+m.right),
37550                 height: centerH - (m.top+m.bottom)
37551             };
37552             //if(this.hideOnLayout){
37553                 //center.el.setStyle("display", "block");
37554             //}
37555             center.updateBox(this.safeBox(centerBox));
37556         }
37557         this.el.repaint();
37558         this.fireEvent("layout", this);
37559     },
37560
37561     // private
37562     safeBox : function(box){
37563         box.width = Math.max(0, box.width);
37564         box.height = Math.max(0, box.height);
37565         return box;
37566     },
37567
37568     /**
37569      * Adds a ContentPanel (or subclass) to this layout.
37570      * @param {String} target The target region key (north, south, east, west or center).
37571      * @param {Roo.ContentPanel} panel The panel to add
37572      * @return {Roo.ContentPanel} The added panel
37573      */
37574     add : function(target, panel){
37575          
37576         target = target.toLowerCase();
37577         return this.regions[target].add(panel);
37578     },
37579
37580     /**
37581      * Remove a ContentPanel (or subclass) to this layout.
37582      * @param {String} target The target region key (north, south, east, west or center).
37583      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37584      * @return {Roo.ContentPanel} The removed panel
37585      */
37586     remove : function(target, panel){
37587         target = target.toLowerCase();
37588         return this.regions[target].remove(panel);
37589     },
37590
37591     /**
37592      * Searches all regions for a panel with the specified id
37593      * @param {String} panelId
37594      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37595      */
37596     findPanel : function(panelId){
37597         var rs = this.regions;
37598         for(var target in rs){
37599             if(typeof rs[target] != "function"){
37600                 var p = rs[target].getPanel(panelId);
37601                 if(p){
37602                     return p;
37603                 }
37604             }
37605         }
37606         return null;
37607     },
37608
37609     /**
37610      * Searches all regions for a panel with the specified id and activates (shows) it.
37611      * @param {String/ContentPanel} panelId The panels id or the panel itself
37612      * @return {Roo.ContentPanel} The shown panel or null
37613      */
37614     showPanel : function(panelId) {
37615       var rs = this.regions;
37616       for(var target in rs){
37617          var r = rs[target];
37618          if(typeof r != "function"){
37619             if(r.hasPanel(panelId)){
37620                return r.showPanel(panelId);
37621             }
37622          }
37623       }
37624       return null;
37625    },
37626
37627    /**
37628      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37629      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37630      */
37631    /*
37632     restoreState : function(provider){
37633         if(!provider){
37634             provider = Roo.state.Manager;
37635         }
37636         var sm = new Roo.LayoutStateManager();
37637         sm.init(this, provider);
37638     },
37639 */
37640  
37641  
37642     /**
37643      * Adds a xtype elements to the layout.
37644      * <pre><code>
37645
37646 layout.addxtype({
37647        xtype : 'ContentPanel',
37648        region: 'west',
37649        items: [ .... ]
37650    }
37651 );
37652
37653 layout.addxtype({
37654         xtype : 'NestedLayoutPanel',
37655         region: 'west',
37656         layout: {
37657            center: { },
37658            west: { }   
37659         },
37660         items : [ ... list of content panels or nested layout panels.. ]
37661    }
37662 );
37663 </code></pre>
37664      * @param {Object} cfg Xtype definition of item to add.
37665      */
37666     addxtype : function(cfg)
37667     {
37668         // basically accepts a pannel...
37669         // can accept a layout region..!?!?
37670         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37671         
37672         
37673         // theory?  children can only be panels??
37674         
37675         //if (!cfg.xtype.match(/Panel$/)) {
37676         //    return false;
37677         //}
37678         var ret = false;
37679         
37680         if (typeof(cfg.region) == 'undefined') {
37681             Roo.log("Failed to add Panel, region was not set");
37682             Roo.log(cfg);
37683             return false;
37684         }
37685         var region = cfg.region;
37686         delete cfg.region;
37687         
37688           
37689         var xitems = [];
37690         if (cfg.items) {
37691             xitems = cfg.items;
37692             delete cfg.items;
37693         }
37694         var nb = false;
37695         
37696         if ( region == 'center') {
37697             Roo.log("Center: " + cfg.title);
37698         }
37699         
37700         
37701         switch(cfg.xtype) 
37702         {
37703             case 'Content':  // ContentPanel (el, cfg)
37704             case 'Scroll':  // ContentPanel (el, cfg)
37705             case 'View': 
37706                 cfg.autoCreate = cfg.autoCreate || true;
37707                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37708                 //} else {
37709                 //    var el = this.el.createChild();
37710                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37711                 //}
37712                 
37713                 this.add(region, ret);
37714                 break;
37715             
37716             /*
37717             case 'TreePanel': // our new panel!
37718                 cfg.el = this.el.createChild();
37719                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37720                 this.add(region, ret);
37721                 break;
37722             */
37723             
37724             case 'Nest': 
37725                 // create a new Layout (which is  a Border Layout...
37726                 
37727                 var clayout = cfg.layout;
37728                 clayout.el  = this.el.createChild();
37729                 clayout.items   = clayout.items  || [];
37730                 
37731                 delete cfg.layout;
37732                 
37733                 // replace this exitems with the clayout ones..
37734                 xitems = clayout.items;
37735                  
37736                 // force background off if it's in center...
37737                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37738                     cfg.background = false;
37739                 }
37740                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37741                 
37742                 
37743                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37744                 //console.log('adding nested layout panel '  + cfg.toSource());
37745                 this.add(region, ret);
37746                 nb = {}; /// find first...
37747                 break;
37748             
37749             case 'Grid':
37750                 
37751                 // needs grid and region
37752                 
37753                 //var el = this.getRegion(region).el.createChild();
37754                 /*
37755                  *var el = this.el.createChild();
37756                 // create the grid first...
37757                 cfg.grid.container = el;
37758                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37759                 */
37760                 
37761                 if (region == 'center' && this.active ) {
37762                     cfg.background = false;
37763                 }
37764                 
37765                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37766                 
37767                 this.add(region, ret);
37768                 /*
37769                 if (cfg.background) {
37770                     // render grid on panel activation (if panel background)
37771                     ret.on('activate', function(gp) {
37772                         if (!gp.grid.rendered) {
37773                     //        gp.grid.render(el);
37774                         }
37775                     });
37776                 } else {
37777                   //  cfg.grid.render(el);
37778                 }
37779                 */
37780                 break;
37781            
37782            
37783             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37784                 // it was the old xcomponent building that caused this before.
37785                 // espeically if border is the top element in the tree.
37786                 ret = this;
37787                 break; 
37788                 
37789                     
37790                 
37791                 
37792                 
37793             default:
37794                 /*
37795                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37796                     
37797                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37798                     this.add(region, ret);
37799                 } else {
37800                 */
37801                     Roo.log(cfg);
37802                     throw "Can not add '" + cfg.xtype + "' to Border";
37803                     return null;
37804              
37805                                 
37806              
37807         }
37808         this.beginUpdate();
37809         // add children..
37810         var region = '';
37811         var abn = {};
37812         Roo.each(xitems, function(i)  {
37813             region = nb && i.region ? i.region : false;
37814             
37815             var add = ret.addxtype(i);
37816            
37817             if (region) {
37818                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37819                 if (!i.background) {
37820                     abn[region] = nb[region] ;
37821                 }
37822             }
37823             
37824         });
37825         this.endUpdate();
37826
37827         // make the last non-background panel active..
37828         //if (nb) { Roo.log(abn); }
37829         if (nb) {
37830             
37831             for(var r in abn) {
37832                 region = this.getRegion(r);
37833                 if (region) {
37834                     // tried using nb[r], but it does not work..
37835                      
37836                     region.showPanel(abn[r]);
37837                    
37838                 }
37839             }
37840         }
37841         return ret;
37842         
37843     },
37844     
37845     
37846 // private
37847     factory : function(cfg)
37848     {
37849         
37850         var validRegions = Roo.bootstrap.layout.Border.regions;
37851
37852         var target = cfg.region;
37853         cfg.mgr = this;
37854         
37855         var r = Roo.bootstrap.layout;
37856         Roo.log(target);
37857         switch(target){
37858             case "north":
37859                 return new r.North(cfg);
37860             case "south":
37861                 return new r.South(cfg);
37862             case "east":
37863                 return new r.East(cfg);
37864             case "west":
37865                 return new r.West(cfg);
37866             case "center":
37867                 return new r.Center(cfg);
37868         }
37869         throw 'Layout region "'+target+'" not supported.';
37870     }
37871     
37872     
37873 });
37874  /*
37875  * Based on:
37876  * Ext JS Library 1.1.1
37877  * Copyright(c) 2006-2007, Ext JS, LLC.
37878  *
37879  * Originally Released Under LGPL - original licence link has changed is not relivant.
37880  *
37881  * Fork - LGPL
37882  * <script type="text/javascript">
37883  */
37884  
37885 /**
37886  * @class Roo.bootstrap.layout.Basic
37887  * @extends Roo.util.Observable
37888  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37889  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37890  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37891  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37892  * @cfg {string}   region  the region that it inhabits..
37893  * @cfg {bool}   skipConfig skip config?
37894  * 
37895
37896  */
37897 Roo.bootstrap.layout.Basic = function(config){
37898     
37899     this.mgr = config.mgr;
37900     
37901     this.position = config.region;
37902     
37903     var skipConfig = config.skipConfig;
37904     
37905     this.events = {
37906         /**
37907          * @scope Roo.BasicLayoutRegion
37908          */
37909         
37910         /**
37911          * @event beforeremove
37912          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37913          * @param {Roo.LayoutRegion} this
37914          * @param {Roo.ContentPanel} panel The panel
37915          * @param {Object} e The cancel event object
37916          */
37917         "beforeremove" : true,
37918         /**
37919          * @event invalidated
37920          * Fires when the layout for this region is changed.
37921          * @param {Roo.LayoutRegion} this
37922          */
37923         "invalidated" : true,
37924         /**
37925          * @event visibilitychange
37926          * Fires when this region is shown or hidden 
37927          * @param {Roo.LayoutRegion} this
37928          * @param {Boolean} visibility true or false
37929          */
37930         "visibilitychange" : true,
37931         /**
37932          * @event paneladded
37933          * Fires when a panel is added. 
37934          * @param {Roo.LayoutRegion} this
37935          * @param {Roo.ContentPanel} panel The panel
37936          */
37937         "paneladded" : true,
37938         /**
37939          * @event panelremoved
37940          * Fires when a panel is removed. 
37941          * @param {Roo.LayoutRegion} this
37942          * @param {Roo.ContentPanel} panel The panel
37943          */
37944         "panelremoved" : true,
37945         /**
37946          * @event beforecollapse
37947          * Fires when this region before collapse.
37948          * @param {Roo.LayoutRegion} this
37949          */
37950         "beforecollapse" : true,
37951         /**
37952          * @event collapsed
37953          * Fires when this region is collapsed.
37954          * @param {Roo.LayoutRegion} this
37955          */
37956         "collapsed" : true,
37957         /**
37958          * @event expanded
37959          * Fires when this region is expanded.
37960          * @param {Roo.LayoutRegion} this
37961          */
37962         "expanded" : true,
37963         /**
37964          * @event slideshow
37965          * Fires when this region is slid into view.
37966          * @param {Roo.LayoutRegion} this
37967          */
37968         "slideshow" : true,
37969         /**
37970          * @event slidehide
37971          * Fires when this region slides out of view. 
37972          * @param {Roo.LayoutRegion} this
37973          */
37974         "slidehide" : true,
37975         /**
37976          * @event panelactivated
37977          * Fires when a panel is activated. 
37978          * @param {Roo.LayoutRegion} this
37979          * @param {Roo.ContentPanel} panel The activated panel
37980          */
37981         "panelactivated" : true,
37982         /**
37983          * @event resized
37984          * Fires when the user resizes this region. 
37985          * @param {Roo.LayoutRegion} this
37986          * @param {Number} newSize The new size (width for east/west, height for north/south)
37987          */
37988         "resized" : true
37989     };
37990     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37991     this.panels = new Roo.util.MixedCollection();
37992     this.panels.getKey = this.getPanelId.createDelegate(this);
37993     this.box = null;
37994     this.activePanel = null;
37995     // ensure listeners are added...
37996     
37997     if (config.listeners || config.events) {
37998         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37999             listeners : config.listeners || {},
38000             events : config.events || {}
38001         });
38002     }
38003     
38004     if(skipConfig !== true){
38005         this.applyConfig(config);
38006     }
38007 };
38008
38009 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38010 {
38011     getPanelId : function(p){
38012         return p.getId();
38013     },
38014     
38015     applyConfig : function(config){
38016         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38017         this.config = config;
38018         
38019     },
38020     
38021     /**
38022      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38023      * the width, for horizontal (north, south) the height.
38024      * @param {Number} newSize The new width or height
38025      */
38026     resizeTo : function(newSize){
38027         var el = this.el ? this.el :
38028                  (this.activePanel ? this.activePanel.getEl() : null);
38029         if(el){
38030             switch(this.position){
38031                 case "east":
38032                 case "west":
38033                     el.setWidth(newSize);
38034                     this.fireEvent("resized", this, newSize);
38035                 break;
38036                 case "north":
38037                 case "south":
38038                     el.setHeight(newSize);
38039                     this.fireEvent("resized", this, newSize);
38040                 break;                
38041             }
38042         }
38043     },
38044     
38045     getBox : function(){
38046         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38047     },
38048     
38049     getMargins : function(){
38050         return this.margins;
38051     },
38052     
38053     updateBox : function(box){
38054         this.box = box;
38055         var el = this.activePanel.getEl();
38056         el.dom.style.left = box.x + "px";
38057         el.dom.style.top = box.y + "px";
38058         this.activePanel.setSize(box.width, box.height);
38059     },
38060     
38061     /**
38062      * Returns the container element for this region.
38063      * @return {Roo.Element}
38064      */
38065     getEl : function(){
38066         return this.activePanel;
38067     },
38068     
38069     /**
38070      * Returns true if this region is currently visible.
38071      * @return {Boolean}
38072      */
38073     isVisible : function(){
38074         return this.activePanel ? true : false;
38075     },
38076     
38077     setActivePanel : function(panel){
38078         panel = this.getPanel(panel);
38079         if(this.activePanel && this.activePanel != panel){
38080             this.activePanel.setActiveState(false);
38081             this.activePanel.getEl().setLeftTop(-10000,-10000);
38082         }
38083         this.activePanel = panel;
38084         panel.setActiveState(true);
38085         if(this.box){
38086             panel.setSize(this.box.width, this.box.height);
38087         }
38088         this.fireEvent("panelactivated", this, panel);
38089         this.fireEvent("invalidated");
38090     },
38091     
38092     /**
38093      * Show the specified panel.
38094      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38095      * @return {Roo.ContentPanel} The shown panel or null
38096      */
38097     showPanel : function(panel){
38098         panel = this.getPanel(panel);
38099         if(panel){
38100             this.setActivePanel(panel);
38101         }
38102         return panel;
38103     },
38104     
38105     /**
38106      * Get the active panel for this region.
38107      * @return {Roo.ContentPanel} The active panel or null
38108      */
38109     getActivePanel : function(){
38110         return this.activePanel;
38111     },
38112     
38113     /**
38114      * Add the passed ContentPanel(s)
38115      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38116      * @return {Roo.ContentPanel} The panel added (if only one was added)
38117      */
38118     add : function(panel){
38119         if(arguments.length > 1){
38120             for(var i = 0, len = arguments.length; i < len; i++) {
38121                 this.add(arguments[i]);
38122             }
38123             return null;
38124         }
38125         if(this.hasPanel(panel)){
38126             this.showPanel(panel);
38127             return panel;
38128         }
38129         var el = panel.getEl();
38130         if(el.dom.parentNode != this.mgr.el.dom){
38131             this.mgr.el.dom.appendChild(el.dom);
38132         }
38133         if(panel.setRegion){
38134             panel.setRegion(this);
38135         }
38136         this.panels.add(panel);
38137         el.setStyle("position", "absolute");
38138         if(!panel.background){
38139             this.setActivePanel(panel);
38140             if(this.config.initialSize && this.panels.getCount()==1){
38141                 this.resizeTo(this.config.initialSize);
38142             }
38143         }
38144         this.fireEvent("paneladded", this, panel);
38145         return panel;
38146     },
38147     
38148     /**
38149      * Returns true if the panel is in this region.
38150      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38151      * @return {Boolean}
38152      */
38153     hasPanel : function(panel){
38154         if(typeof panel == "object"){ // must be panel obj
38155             panel = panel.getId();
38156         }
38157         return this.getPanel(panel) ? true : false;
38158     },
38159     
38160     /**
38161      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38162      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38163      * @param {Boolean} preservePanel Overrides the config preservePanel option
38164      * @return {Roo.ContentPanel} The panel that was removed
38165      */
38166     remove : function(panel, preservePanel){
38167         panel = this.getPanel(panel);
38168         if(!panel){
38169             return null;
38170         }
38171         var e = {};
38172         this.fireEvent("beforeremove", this, panel, e);
38173         if(e.cancel === true){
38174             return null;
38175         }
38176         var panelId = panel.getId();
38177         this.panels.removeKey(panelId);
38178         return panel;
38179     },
38180     
38181     /**
38182      * Returns the panel specified or null if it's not in this region.
38183      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38184      * @return {Roo.ContentPanel}
38185      */
38186     getPanel : function(id){
38187         if(typeof id == "object"){ // must be panel obj
38188             return id;
38189         }
38190         return this.panels.get(id);
38191     },
38192     
38193     /**
38194      * Returns this regions position (north/south/east/west/center).
38195      * @return {String} 
38196      */
38197     getPosition: function(){
38198         return this.position;    
38199     }
38200 });/*
38201  * Based on:
38202  * Ext JS Library 1.1.1
38203  * Copyright(c) 2006-2007, Ext JS, LLC.
38204  *
38205  * Originally Released Under LGPL - original licence link has changed is not relivant.
38206  *
38207  * Fork - LGPL
38208  * <script type="text/javascript">
38209  */
38210  
38211 /**
38212  * @class Roo.bootstrap.layout.Region
38213  * @extends Roo.bootstrap.layout.Basic
38214  * This class represents a region in a layout manager.
38215  
38216  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38217  * @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})
38218  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38219  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38220  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38221  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38222  * @cfg {String}    title           The title for the region (overrides panel titles)
38223  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38224  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38225  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38226  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38227  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38228  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38229  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38230  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38231  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38232  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38233
38234  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38235  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38236  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38237  * @cfg {Number}    width           For East/West panels
38238  * @cfg {Number}    height          For North/South panels
38239  * @cfg {Boolean}   split           To show the splitter
38240  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38241  * 
38242  * @cfg {string}   cls             Extra CSS classes to add to region
38243  * 
38244  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38245  * @cfg {string}   region  the region that it inhabits..
38246  *
38247
38248  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38249  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38250
38251  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38252  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38253  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38254  */
38255 Roo.bootstrap.layout.Region = function(config)
38256 {
38257     this.applyConfig(config);
38258
38259     var mgr = config.mgr;
38260     var pos = config.region;
38261     config.skipConfig = true;
38262     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38263     
38264     if (mgr.el) {
38265         this.onRender(mgr.el);   
38266     }
38267      
38268     this.visible = true;
38269     this.collapsed = false;
38270     this.unrendered_panels = [];
38271 };
38272
38273 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38274
38275     position: '', // set by wrapper (eg. north/south etc..)
38276     unrendered_panels : null,  // unrendered panels.
38277     
38278     tabPosition : false,
38279     
38280     mgr: false, // points to 'Border'
38281     
38282     
38283     createBody : function(){
38284         /** This region's body element 
38285         * @type Roo.Element */
38286         this.bodyEl = this.el.createChild({
38287                 tag: "div",
38288                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38289         });
38290     },
38291
38292     onRender: function(ctr, pos)
38293     {
38294         var dh = Roo.DomHelper;
38295         /** This region's container element 
38296         * @type Roo.Element */
38297         this.el = dh.append(ctr.dom, {
38298                 tag: "div",
38299                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38300             }, true);
38301         /** This region's title element 
38302         * @type Roo.Element */
38303     
38304         this.titleEl = dh.append(this.el.dom,  {
38305                 tag: "div",
38306                 unselectable: "on",
38307                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38308                 children:[
38309                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38310                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38311                 ]
38312             }, true);
38313         
38314         this.titleEl.enableDisplayMode();
38315         /** This region's title text element 
38316         * @type HTMLElement */
38317         this.titleTextEl = this.titleEl.dom.firstChild;
38318         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38319         /*
38320         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38321         this.closeBtn.enableDisplayMode();
38322         this.closeBtn.on("click", this.closeClicked, this);
38323         this.closeBtn.hide();
38324     */
38325         this.createBody(this.config);
38326         if(this.config.hideWhenEmpty){
38327             this.hide();
38328             this.on("paneladded", this.validateVisibility, this);
38329             this.on("panelremoved", this.validateVisibility, this);
38330         }
38331         if(this.autoScroll){
38332             this.bodyEl.setStyle("overflow", "auto");
38333         }else{
38334             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38335         }
38336         //if(c.titlebar !== false){
38337             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38338                 this.titleEl.hide();
38339             }else{
38340                 this.titleEl.show();
38341                 if(this.config.title){
38342                     this.titleTextEl.innerHTML = this.config.title;
38343                 }
38344             }
38345         //}
38346         if(this.config.collapsed){
38347             this.collapse(true);
38348         }
38349         if(this.config.hidden){
38350             this.hide();
38351         }
38352         
38353         if (this.unrendered_panels && this.unrendered_panels.length) {
38354             for (var i =0;i< this.unrendered_panels.length; i++) {
38355                 this.add(this.unrendered_panels[i]);
38356             }
38357             this.unrendered_panels = null;
38358             
38359         }
38360         
38361     },
38362     
38363     applyConfig : function(c)
38364     {
38365         /*
38366          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38367             var dh = Roo.DomHelper;
38368             if(c.titlebar !== false){
38369                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38370                 this.collapseBtn.on("click", this.collapse, this);
38371                 this.collapseBtn.enableDisplayMode();
38372                 /*
38373                 if(c.showPin === true || this.showPin){
38374                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38375                     this.stickBtn.enableDisplayMode();
38376                     this.stickBtn.on("click", this.expand, this);
38377                     this.stickBtn.hide();
38378                 }
38379                 
38380             }
38381             */
38382             /** This region's collapsed element
38383             * @type Roo.Element */
38384             /*
38385              *
38386             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38387                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38388             ]}, true);
38389             
38390             if(c.floatable !== false){
38391                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38392                this.collapsedEl.on("click", this.collapseClick, this);
38393             }
38394
38395             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38396                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38397                    id: "message", unselectable: "on", style:{"float":"left"}});
38398                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38399              }
38400             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38401             this.expandBtn.on("click", this.expand, this);
38402             
38403         }
38404         
38405         if(this.collapseBtn){
38406             this.collapseBtn.setVisible(c.collapsible == true);
38407         }
38408         
38409         this.cmargins = c.cmargins || this.cmargins ||
38410                          (this.position == "west" || this.position == "east" ?
38411                              {top: 0, left: 2, right:2, bottom: 0} :
38412                              {top: 2, left: 0, right:0, bottom: 2});
38413         */
38414         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38415         
38416         
38417         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38418         
38419         this.autoScroll = c.autoScroll || false;
38420         
38421         
38422        
38423         
38424         this.duration = c.duration || .30;
38425         this.slideDuration = c.slideDuration || .45;
38426         this.config = c;
38427        
38428     },
38429     /**
38430      * Returns true if this region is currently visible.
38431      * @return {Boolean}
38432      */
38433     isVisible : function(){
38434         return this.visible;
38435     },
38436
38437     /**
38438      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38439      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38440      */
38441     //setCollapsedTitle : function(title){
38442     //    title = title || "&#160;";
38443      //   if(this.collapsedTitleTextEl){
38444       //      this.collapsedTitleTextEl.innerHTML = title;
38445        // }
38446     //},
38447
38448     getBox : function(){
38449         var b;
38450       //  if(!this.collapsed){
38451             b = this.el.getBox(false, true);
38452        // }else{
38453           //  b = this.collapsedEl.getBox(false, true);
38454         //}
38455         return b;
38456     },
38457
38458     getMargins : function(){
38459         return this.margins;
38460         //return this.collapsed ? this.cmargins : this.margins;
38461     },
38462 /*
38463     highlight : function(){
38464         this.el.addClass("x-layout-panel-dragover");
38465     },
38466
38467     unhighlight : function(){
38468         this.el.removeClass("x-layout-panel-dragover");
38469     },
38470 */
38471     updateBox : function(box)
38472     {
38473         if (!this.bodyEl) {
38474             return; // not rendered yet..
38475         }
38476         
38477         this.box = box;
38478         if(!this.collapsed){
38479             this.el.dom.style.left = box.x + "px";
38480             this.el.dom.style.top = box.y + "px";
38481             this.updateBody(box.width, box.height);
38482         }else{
38483             this.collapsedEl.dom.style.left = box.x + "px";
38484             this.collapsedEl.dom.style.top = box.y + "px";
38485             this.collapsedEl.setSize(box.width, box.height);
38486         }
38487         if(this.tabs){
38488             this.tabs.autoSizeTabs();
38489         }
38490     },
38491
38492     updateBody : function(w, h)
38493     {
38494         if(w !== null){
38495             this.el.setWidth(w);
38496             w -= this.el.getBorderWidth("rl");
38497             if(this.config.adjustments){
38498                 w += this.config.adjustments[0];
38499             }
38500         }
38501         if(h !== null && h > 0){
38502             this.el.setHeight(h);
38503             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38504             h -= this.el.getBorderWidth("tb");
38505             if(this.config.adjustments){
38506                 h += this.config.adjustments[1];
38507             }
38508             this.bodyEl.setHeight(h);
38509             if(this.tabs){
38510                 h = this.tabs.syncHeight(h);
38511             }
38512         }
38513         if(this.panelSize){
38514             w = w !== null ? w : this.panelSize.width;
38515             h = h !== null ? h : this.panelSize.height;
38516         }
38517         if(this.activePanel){
38518             var el = this.activePanel.getEl();
38519             w = w !== null ? w : el.getWidth();
38520             h = h !== null ? h : el.getHeight();
38521             this.panelSize = {width: w, height: h};
38522             this.activePanel.setSize(w, h);
38523         }
38524         if(Roo.isIE && this.tabs){
38525             this.tabs.el.repaint();
38526         }
38527     },
38528
38529     /**
38530      * Returns the container element for this region.
38531      * @return {Roo.Element}
38532      */
38533     getEl : function(){
38534         return this.el;
38535     },
38536
38537     /**
38538      * Hides this region.
38539      */
38540     hide : function(){
38541         //if(!this.collapsed){
38542             this.el.dom.style.left = "-2000px";
38543             this.el.hide();
38544         //}else{
38545          //   this.collapsedEl.dom.style.left = "-2000px";
38546          //   this.collapsedEl.hide();
38547        // }
38548         this.visible = false;
38549         this.fireEvent("visibilitychange", this, false);
38550     },
38551
38552     /**
38553      * Shows this region if it was previously hidden.
38554      */
38555     show : function(){
38556         //if(!this.collapsed){
38557             this.el.show();
38558         //}else{
38559         //    this.collapsedEl.show();
38560        // }
38561         this.visible = true;
38562         this.fireEvent("visibilitychange", this, true);
38563     },
38564 /*
38565     closeClicked : function(){
38566         if(this.activePanel){
38567             this.remove(this.activePanel);
38568         }
38569     },
38570
38571     collapseClick : function(e){
38572         if(this.isSlid){
38573            e.stopPropagation();
38574            this.slideIn();
38575         }else{
38576            e.stopPropagation();
38577            this.slideOut();
38578         }
38579     },
38580 */
38581     /**
38582      * Collapses this region.
38583      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38584      */
38585     /*
38586     collapse : function(skipAnim, skipCheck = false){
38587         if(this.collapsed) {
38588             return;
38589         }
38590         
38591         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38592             
38593             this.collapsed = true;
38594             if(this.split){
38595                 this.split.el.hide();
38596             }
38597             if(this.config.animate && skipAnim !== true){
38598                 this.fireEvent("invalidated", this);
38599                 this.animateCollapse();
38600             }else{
38601                 this.el.setLocation(-20000,-20000);
38602                 this.el.hide();
38603                 this.collapsedEl.show();
38604                 this.fireEvent("collapsed", this);
38605                 this.fireEvent("invalidated", this);
38606             }
38607         }
38608         
38609     },
38610 */
38611     animateCollapse : function(){
38612         // overridden
38613     },
38614
38615     /**
38616      * Expands this region if it was previously collapsed.
38617      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38618      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38619      */
38620     /*
38621     expand : function(e, skipAnim){
38622         if(e) {
38623             e.stopPropagation();
38624         }
38625         if(!this.collapsed || this.el.hasActiveFx()) {
38626             return;
38627         }
38628         if(this.isSlid){
38629             this.afterSlideIn();
38630             skipAnim = true;
38631         }
38632         this.collapsed = false;
38633         if(this.config.animate && skipAnim !== true){
38634             this.animateExpand();
38635         }else{
38636             this.el.show();
38637             if(this.split){
38638                 this.split.el.show();
38639             }
38640             this.collapsedEl.setLocation(-2000,-2000);
38641             this.collapsedEl.hide();
38642             this.fireEvent("invalidated", this);
38643             this.fireEvent("expanded", this);
38644         }
38645     },
38646 */
38647     animateExpand : function(){
38648         // overridden
38649     },
38650
38651     initTabs : function()
38652     {
38653         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38654         
38655         var ts = new Roo.bootstrap.panel.Tabs({
38656             el: this.bodyEl.dom,
38657             region : this,
38658             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38659             disableTooltips: this.config.disableTabTips,
38660             toolbar : this.config.toolbar
38661         });
38662         
38663         if(this.config.hideTabs){
38664             ts.stripWrap.setDisplayed(false);
38665         }
38666         this.tabs = ts;
38667         ts.resizeTabs = this.config.resizeTabs === true;
38668         ts.minTabWidth = this.config.minTabWidth || 40;
38669         ts.maxTabWidth = this.config.maxTabWidth || 250;
38670         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38671         ts.monitorResize = false;
38672         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38673         ts.bodyEl.addClass('roo-layout-tabs-body');
38674         this.panels.each(this.initPanelAsTab, this);
38675     },
38676
38677     initPanelAsTab : function(panel){
38678         var ti = this.tabs.addTab(
38679             panel.getEl().id,
38680             panel.getTitle(),
38681             null,
38682             this.config.closeOnTab && panel.isClosable(),
38683             panel.tpl
38684         );
38685         if(panel.tabTip !== undefined){
38686             ti.setTooltip(panel.tabTip);
38687         }
38688         ti.on("activate", function(){
38689               this.setActivePanel(panel);
38690         }, this);
38691         
38692         if(this.config.closeOnTab){
38693             ti.on("beforeclose", function(t, e){
38694                 e.cancel = true;
38695                 this.remove(panel);
38696             }, this);
38697         }
38698         
38699         panel.tabItem = ti;
38700         
38701         return ti;
38702     },
38703
38704     updatePanelTitle : function(panel, title)
38705     {
38706         if(this.activePanel == panel){
38707             this.updateTitle(title);
38708         }
38709         if(this.tabs){
38710             var ti = this.tabs.getTab(panel.getEl().id);
38711             ti.setText(title);
38712             if(panel.tabTip !== undefined){
38713                 ti.setTooltip(panel.tabTip);
38714             }
38715         }
38716     },
38717
38718     updateTitle : function(title){
38719         if(this.titleTextEl && !this.config.title){
38720             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38721         }
38722     },
38723
38724     setActivePanel : function(panel)
38725     {
38726         panel = this.getPanel(panel);
38727         if(this.activePanel && this.activePanel != panel){
38728             if(this.activePanel.setActiveState(false) === false){
38729                 return;
38730             }
38731         }
38732         this.activePanel = panel;
38733         panel.setActiveState(true);
38734         if(this.panelSize){
38735             panel.setSize(this.panelSize.width, this.panelSize.height);
38736         }
38737         if(this.closeBtn){
38738             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38739         }
38740         this.updateTitle(panel.getTitle());
38741         if(this.tabs){
38742             this.fireEvent("invalidated", this);
38743         }
38744         this.fireEvent("panelactivated", this, panel);
38745     },
38746
38747     /**
38748      * Shows the specified panel.
38749      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38750      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38751      */
38752     showPanel : function(panel)
38753     {
38754         panel = this.getPanel(panel);
38755         if(panel){
38756             if(this.tabs){
38757                 var tab = this.tabs.getTab(panel.getEl().id);
38758                 if(tab.isHidden()){
38759                     this.tabs.unhideTab(tab.id);
38760                 }
38761                 tab.activate();
38762             }else{
38763                 this.setActivePanel(panel);
38764             }
38765         }
38766         return panel;
38767     },
38768
38769     /**
38770      * Get the active panel for this region.
38771      * @return {Roo.ContentPanel} The active panel or null
38772      */
38773     getActivePanel : function(){
38774         return this.activePanel;
38775     },
38776
38777     validateVisibility : function(){
38778         if(this.panels.getCount() < 1){
38779             this.updateTitle("&#160;");
38780             this.closeBtn.hide();
38781             this.hide();
38782         }else{
38783             if(!this.isVisible()){
38784                 this.show();
38785             }
38786         }
38787     },
38788
38789     /**
38790      * Adds the passed ContentPanel(s) to this region.
38791      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38792      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38793      */
38794     add : function(panel)
38795     {
38796         if(arguments.length > 1){
38797             for(var i = 0, len = arguments.length; i < len; i++) {
38798                 this.add(arguments[i]);
38799             }
38800             return null;
38801         }
38802         
38803         // if we have not been rendered yet, then we can not really do much of this..
38804         if (!this.bodyEl) {
38805             this.unrendered_panels.push(panel);
38806             return panel;
38807         }
38808         
38809         
38810         
38811         
38812         if(this.hasPanel(panel)){
38813             this.showPanel(panel);
38814             return panel;
38815         }
38816         panel.setRegion(this);
38817         this.panels.add(panel);
38818        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38819             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38820             // and hide them... ???
38821             this.bodyEl.dom.appendChild(panel.getEl().dom);
38822             if(panel.background !== true){
38823                 this.setActivePanel(panel);
38824             }
38825             this.fireEvent("paneladded", this, panel);
38826             return panel;
38827         }
38828         */
38829         if(!this.tabs){
38830             this.initTabs();
38831         }else{
38832             this.initPanelAsTab(panel);
38833         }
38834         
38835         
38836         if(panel.background !== true){
38837             this.tabs.activate(panel.getEl().id);
38838         }
38839         this.fireEvent("paneladded", this, panel);
38840         return panel;
38841     },
38842
38843     /**
38844      * Hides the tab for the specified panel.
38845      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38846      */
38847     hidePanel : function(panel){
38848         if(this.tabs && (panel = this.getPanel(panel))){
38849             this.tabs.hideTab(panel.getEl().id);
38850         }
38851     },
38852
38853     /**
38854      * Unhides the tab for a previously hidden panel.
38855      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38856      */
38857     unhidePanel : function(panel){
38858         if(this.tabs && (panel = this.getPanel(panel))){
38859             this.tabs.unhideTab(panel.getEl().id);
38860         }
38861     },
38862
38863     clearPanels : function(){
38864         while(this.panels.getCount() > 0){
38865              this.remove(this.panels.first());
38866         }
38867     },
38868
38869     /**
38870      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38871      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38872      * @param {Boolean} preservePanel Overrides the config preservePanel option
38873      * @return {Roo.ContentPanel} The panel that was removed
38874      */
38875     remove : function(panel, preservePanel)
38876     {
38877         panel = this.getPanel(panel);
38878         if(!panel){
38879             return null;
38880         }
38881         var e = {};
38882         this.fireEvent("beforeremove", this, panel, e);
38883         if(e.cancel === true){
38884             return null;
38885         }
38886         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38887         var panelId = panel.getId();
38888         this.panels.removeKey(panelId);
38889         if(preservePanel){
38890             document.body.appendChild(panel.getEl().dom);
38891         }
38892         if(this.tabs){
38893             this.tabs.removeTab(panel.getEl().id);
38894         }else if (!preservePanel){
38895             this.bodyEl.dom.removeChild(panel.getEl().dom);
38896         }
38897         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38898             var p = this.panels.first();
38899             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38900             tempEl.appendChild(p.getEl().dom);
38901             this.bodyEl.update("");
38902             this.bodyEl.dom.appendChild(p.getEl().dom);
38903             tempEl = null;
38904             this.updateTitle(p.getTitle());
38905             this.tabs = null;
38906             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38907             this.setActivePanel(p);
38908         }
38909         panel.setRegion(null);
38910         if(this.activePanel == panel){
38911             this.activePanel = null;
38912         }
38913         if(this.config.autoDestroy !== false && preservePanel !== true){
38914             try{panel.destroy();}catch(e){}
38915         }
38916         this.fireEvent("panelremoved", this, panel);
38917         return panel;
38918     },
38919
38920     /**
38921      * Returns the TabPanel component used by this region
38922      * @return {Roo.TabPanel}
38923      */
38924     getTabs : function(){
38925         return this.tabs;
38926     },
38927
38928     createTool : function(parentEl, className){
38929         var btn = Roo.DomHelper.append(parentEl, {
38930             tag: "div",
38931             cls: "x-layout-tools-button",
38932             children: [ {
38933                 tag: "div",
38934                 cls: "roo-layout-tools-button-inner " + className,
38935                 html: "&#160;"
38936             }]
38937         }, true);
38938         btn.addClassOnOver("roo-layout-tools-button-over");
38939         return btn;
38940     }
38941 });/*
38942  * Based on:
38943  * Ext JS Library 1.1.1
38944  * Copyright(c) 2006-2007, Ext JS, LLC.
38945  *
38946  * Originally Released Under LGPL - original licence link has changed is not relivant.
38947  *
38948  * Fork - LGPL
38949  * <script type="text/javascript">
38950  */
38951  
38952
38953
38954 /**
38955  * @class Roo.SplitLayoutRegion
38956  * @extends Roo.LayoutRegion
38957  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38958  */
38959 Roo.bootstrap.layout.Split = function(config){
38960     this.cursor = config.cursor;
38961     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38962 };
38963
38964 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38965 {
38966     splitTip : "Drag to resize.",
38967     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38968     useSplitTips : false,
38969
38970     applyConfig : function(config){
38971         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38972     },
38973     
38974     onRender : function(ctr,pos) {
38975         
38976         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38977         if(!this.config.split){
38978             return;
38979         }
38980         if(!this.split){
38981             
38982             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38983                             tag: "div",
38984                             id: this.el.id + "-split",
38985                             cls: "roo-layout-split roo-layout-split-"+this.position,
38986                             html: "&#160;"
38987             });
38988             /** The SplitBar for this region 
38989             * @type Roo.SplitBar */
38990             // does not exist yet...
38991             Roo.log([this.position, this.orientation]);
38992             
38993             this.split = new Roo.bootstrap.SplitBar({
38994                 dragElement : splitEl,
38995                 resizingElement: this.el,
38996                 orientation : this.orientation
38997             });
38998             
38999             this.split.on("moved", this.onSplitMove, this);
39000             this.split.useShim = this.config.useShim === true;
39001             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39002             if(this.useSplitTips){
39003                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39004             }
39005             //if(config.collapsible){
39006             //    this.split.el.on("dblclick", this.collapse,  this);
39007             //}
39008         }
39009         if(typeof this.config.minSize != "undefined"){
39010             this.split.minSize = this.config.minSize;
39011         }
39012         if(typeof this.config.maxSize != "undefined"){
39013             this.split.maxSize = this.config.maxSize;
39014         }
39015         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39016             this.hideSplitter();
39017         }
39018         
39019     },
39020
39021     getHMaxSize : function(){
39022          var cmax = this.config.maxSize || 10000;
39023          var center = this.mgr.getRegion("center");
39024          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39025     },
39026
39027     getVMaxSize : function(){
39028          var cmax = this.config.maxSize || 10000;
39029          var center = this.mgr.getRegion("center");
39030          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39031     },
39032
39033     onSplitMove : function(split, newSize){
39034         this.fireEvent("resized", this, newSize);
39035     },
39036     
39037     /** 
39038      * Returns the {@link Roo.SplitBar} for this region.
39039      * @return {Roo.SplitBar}
39040      */
39041     getSplitBar : function(){
39042         return this.split;
39043     },
39044     
39045     hide : function(){
39046         this.hideSplitter();
39047         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39048     },
39049
39050     hideSplitter : function(){
39051         if(this.split){
39052             this.split.el.setLocation(-2000,-2000);
39053             this.split.el.hide();
39054         }
39055     },
39056
39057     show : function(){
39058         if(this.split){
39059             this.split.el.show();
39060         }
39061         Roo.bootstrap.layout.Split.superclass.show.call(this);
39062     },
39063     
39064     beforeSlide: function(){
39065         if(Roo.isGecko){// firefox overflow auto bug workaround
39066             this.bodyEl.clip();
39067             if(this.tabs) {
39068                 this.tabs.bodyEl.clip();
39069             }
39070             if(this.activePanel){
39071                 this.activePanel.getEl().clip();
39072                 
39073                 if(this.activePanel.beforeSlide){
39074                     this.activePanel.beforeSlide();
39075                 }
39076             }
39077         }
39078     },
39079     
39080     afterSlide : function(){
39081         if(Roo.isGecko){// firefox overflow auto bug workaround
39082             this.bodyEl.unclip();
39083             if(this.tabs) {
39084                 this.tabs.bodyEl.unclip();
39085             }
39086             if(this.activePanel){
39087                 this.activePanel.getEl().unclip();
39088                 if(this.activePanel.afterSlide){
39089                     this.activePanel.afterSlide();
39090                 }
39091             }
39092         }
39093     },
39094
39095     initAutoHide : function(){
39096         if(this.autoHide !== false){
39097             if(!this.autoHideHd){
39098                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39099                 this.autoHideHd = {
39100                     "mouseout": function(e){
39101                         if(!e.within(this.el, true)){
39102                             st.delay(500);
39103                         }
39104                     },
39105                     "mouseover" : function(e){
39106                         st.cancel();
39107                     },
39108                     scope : this
39109                 };
39110             }
39111             this.el.on(this.autoHideHd);
39112         }
39113     },
39114
39115     clearAutoHide : function(){
39116         if(this.autoHide !== false){
39117             this.el.un("mouseout", this.autoHideHd.mouseout);
39118             this.el.un("mouseover", this.autoHideHd.mouseover);
39119         }
39120     },
39121
39122     clearMonitor : function(){
39123         Roo.get(document).un("click", this.slideInIf, this);
39124     },
39125
39126     // these names are backwards but not changed for compat
39127     slideOut : function(){
39128         if(this.isSlid || this.el.hasActiveFx()){
39129             return;
39130         }
39131         this.isSlid = true;
39132         if(this.collapseBtn){
39133             this.collapseBtn.hide();
39134         }
39135         this.closeBtnState = this.closeBtn.getStyle('display');
39136         this.closeBtn.hide();
39137         if(this.stickBtn){
39138             this.stickBtn.show();
39139         }
39140         this.el.show();
39141         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39142         this.beforeSlide();
39143         this.el.setStyle("z-index", 10001);
39144         this.el.slideIn(this.getSlideAnchor(), {
39145             callback: function(){
39146                 this.afterSlide();
39147                 this.initAutoHide();
39148                 Roo.get(document).on("click", this.slideInIf, this);
39149                 this.fireEvent("slideshow", this);
39150             },
39151             scope: this,
39152             block: true
39153         });
39154     },
39155
39156     afterSlideIn : function(){
39157         this.clearAutoHide();
39158         this.isSlid = false;
39159         this.clearMonitor();
39160         this.el.setStyle("z-index", "");
39161         if(this.collapseBtn){
39162             this.collapseBtn.show();
39163         }
39164         this.closeBtn.setStyle('display', this.closeBtnState);
39165         if(this.stickBtn){
39166             this.stickBtn.hide();
39167         }
39168         this.fireEvent("slidehide", this);
39169     },
39170
39171     slideIn : function(cb){
39172         if(!this.isSlid || this.el.hasActiveFx()){
39173             Roo.callback(cb);
39174             return;
39175         }
39176         this.isSlid = false;
39177         this.beforeSlide();
39178         this.el.slideOut(this.getSlideAnchor(), {
39179             callback: function(){
39180                 this.el.setLeftTop(-10000, -10000);
39181                 this.afterSlide();
39182                 this.afterSlideIn();
39183                 Roo.callback(cb);
39184             },
39185             scope: this,
39186             block: true
39187         });
39188     },
39189     
39190     slideInIf : function(e){
39191         if(!e.within(this.el)){
39192             this.slideIn();
39193         }
39194     },
39195
39196     animateCollapse : function(){
39197         this.beforeSlide();
39198         this.el.setStyle("z-index", 20000);
39199         var anchor = this.getSlideAnchor();
39200         this.el.slideOut(anchor, {
39201             callback : function(){
39202                 this.el.setStyle("z-index", "");
39203                 this.collapsedEl.slideIn(anchor, {duration:.3});
39204                 this.afterSlide();
39205                 this.el.setLocation(-10000,-10000);
39206                 this.el.hide();
39207                 this.fireEvent("collapsed", this);
39208             },
39209             scope: this,
39210             block: true
39211         });
39212     },
39213
39214     animateExpand : function(){
39215         this.beforeSlide();
39216         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39217         this.el.setStyle("z-index", 20000);
39218         this.collapsedEl.hide({
39219             duration:.1
39220         });
39221         this.el.slideIn(this.getSlideAnchor(), {
39222             callback : function(){
39223                 this.el.setStyle("z-index", "");
39224                 this.afterSlide();
39225                 if(this.split){
39226                     this.split.el.show();
39227                 }
39228                 this.fireEvent("invalidated", this);
39229                 this.fireEvent("expanded", this);
39230             },
39231             scope: this,
39232             block: true
39233         });
39234     },
39235
39236     anchors : {
39237         "west" : "left",
39238         "east" : "right",
39239         "north" : "top",
39240         "south" : "bottom"
39241     },
39242
39243     sanchors : {
39244         "west" : "l",
39245         "east" : "r",
39246         "north" : "t",
39247         "south" : "b"
39248     },
39249
39250     canchors : {
39251         "west" : "tl-tr",
39252         "east" : "tr-tl",
39253         "north" : "tl-bl",
39254         "south" : "bl-tl"
39255     },
39256
39257     getAnchor : function(){
39258         return this.anchors[this.position];
39259     },
39260
39261     getCollapseAnchor : function(){
39262         return this.canchors[this.position];
39263     },
39264
39265     getSlideAnchor : function(){
39266         return this.sanchors[this.position];
39267     },
39268
39269     getAlignAdj : function(){
39270         var cm = this.cmargins;
39271         switch(this.position){
39272             case "west":
39273                 return [0, 0];
39274             break;
39275             case "east":
39276                 return [0, 0];
39277             break;
39278             case "north":
39279                 return [0, 0];
39280             break;
39281             case "south":
39282                 return [0, 0];
39283             break;
39284         }
39285     },
39286
39287     getExpandAdj : function(){
39288         var c = this.collapsedEl, cm = this.cmargins;
39289         switch(this.position){
39290             case "west":
39291                 return [-(cm.right+c.getWidth()+cm.left), 0];
39292             break;
39293             case "east":
39294                 return [cm.right+c.getWidth()+cm.left, 0];
39295             break;
39296             case "north":
39297                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39298             break;
39299             case "south":
39300                 return [0, cm.top+cm.bottom+c.getHeight()];
39301             break;
39302         }
39303     }
39304 });/*
39305  * Based on:
39306  * Ext JS Library 1.1.1
39307  * Copyright(c) 2006-2007, Ext JS, LLC.
39308  *
39309  * Originally Released Under LGPL - original licence link has changed is not relivant.
39310  *
39311  * Fork - LGPL
39312  * <script type="text/javascript">
39313  */
39314 /*
39315  * These classes are private internal classes
39316  */
39317 Roo.bootstrap.layout.Center = function(config){
39318     config.region = "center";
39319     Roo.bootstrap.layout.Region.call(this, config);
39320     this.visible = true;
39321     this.minWidth = config.minWidth || 20;
39322     this.minHeight = config.minHeight || 20;
39323 };
39324
39325 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39326     hide : function(){
39327         // center panel can't be hidden
39328     },
39329     
39330     show : function(){
39331         // center panel can't be hidden
39332     },
39333     
39334     getMinWidth: function(){
39335         return this.minWidth;
39336     },
39337     
39338     getMinHeight: function(){
39339         return this.minHeight;
39340     }
39341 });
39342
39343
39344
39345
39346  
39347
39348
39349
39350
39351
39352
39353 Roo.bootstrap.layout.North = function(config)
39354 {
39355     config.region = 'north';
39356     config.cursor = 'n-resize';
39357     
39358     Roo.bootstrap.layout.Split.call(this, config);
39359     
39360     
39361     if(this.split){
39362         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39363         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39364         this.split.el.addClass("roo-layout-split-v");
39365     }
39366     //var size = config.initialSize || config.height;
39367     //if(this.el && typeof size != "undefined"){
39368     //    this.el.setHeight(size);
39369     //}
39370 };
39371 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39372 {
39373     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39374      
39375      
39376     onRender : function(ctr, pos)
39377     {
39378         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39379         var size = this.config.initialSize || this.config.height;
39380         if(this.el && typeof size != "undefined"){
39381             this.el.setHeight(size);
39382         }
39383     
39384     },
39385     
39386     getBox : function(){
39387         if(this.collapsed){
39388             return this.collapsedEl.getBox();
39389         }
39390         var box = this.el.getBox();
39391         if(this.split){
39392             box.height += this.split.el.getHeight();
39393         }
39394         return box;
39395     },
39396     
39397     updateBox : function(box){
39398         if(this.split && !this.collapsed){
39399             box.height -= this.split.el.getHeight();
39400             this.split.el.setLeft(box.x);
39401             this.split.el.setTop(box.y+box.height);
39402             this.split.el.setWidth(box.width);
39403         }
39404         if(this.collapsed){
39405             this.updateBody(box.width, null);
39406         }
39407         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39408     }
39409 });
39410
39411
39412
39413
39414
39415 Roo.bootstrap.layout.South = function(config){
39416     config.region = 'south';
39417     config.cursor = 's-resize';
39418     Roo.bootstrap.layout.Split.call(this, config);
39419     if(this.split){
39420         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39421         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39422         this.split.el.addClass("roo-layout-split-v");
39423     }
39424     
39425 };
39426
39427 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39428     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39429     
39430     onRender : function(ctr, pos)
39431     {
39432         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39433         var size = this.config.initialSize || this.config.height;
39434         if(this.el && typeof size != "undefined"){
39435             this.el.setHeight(size);
39436         }
39437     
39438     },
39439     
39440     getBox : function(){
39441         if(this.collapsed){
39442             return this.collapsedEl.getBox();
39443         }
39444         var box = this.el.getBox();
39445         if(this.split){
39446             var sh = this.split.el.getHeight();
39447             box.height += sh;
39448             box.y -= sh;
39449         }
39450         return box;
39451     },
39452     
39453     updateBox : function(box){
39454         if(this.split && !this.collapsed){
39455             var sh = this.split.el.getHeight();
39456             box.height -= sh;
39457             box.y += sh;
39458             this.split.el.setLeft(box.x);
39459             this.split.el.setTop(box.y-sh);
39460             this.split.el.setWidth(box.width);
39461         }
39462         if(this.collapsed){
39463             this.updateBody(box.width, null);
39464         }
39465         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39466     }
39467 });
39468
39469 Roo.bootstrap.layout.East = function(config){
39470     config.region = "east";
39471     config.cursor = "e-resize";
39472     Roo.bootstrap.layout.Split.call(this, config);
39473     if(this.split){
39474         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39475         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39476         this.split.el.addClass("roo-layout-split-h");
39477     }
39478     
39479 };
39480 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39481     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39482     
39483     onRender : function(ctr, pos)
39484     {
39485         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39486         var size = this.config.initialSize || this.config.width;
39487         if(this.el && typeof size != "undefined"){
39488             this.el.setWidth(size);
39489         }
39490     
39491     },
39492     
39493     getBox : function(){
39494         if(this.collapsed){
39495             return this.collapsedEl.getBox();
39496         }
39497         var box = this.el.getBox();
39498         if(this.split){
39499             var sw = this.split.el.getWidth();
39500             box.width += sw;
39501             box.x -= sw;
39502         }
39503         return box;
39504     },
39505
39506     updateBox : function(box){
39507         if(this.split && !this.collapsed){
39508             var sw = this.split.el.getWidth();
39509             box.width -= sw;
39510             this.split.el.setLeft(box.x);
39511             this.split.el.setTop(box.y);
39512             this.split.el.setHeight(box.height);
39513             box.x += sw;
39514         }
39515         if(this.collapsed){
39516             this.updateBody(null, box.height);
39517         }
39518         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39519     }
39520 });
39521
39522 Roo.bootstrap.layout.West = function(config){
39523     config.region = "west";
39524     config.cursor = "w-resize";
39525     
39526     Roo.bootstrap.layout.Split.call(this, config);
39527     if(this.split){
39528         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39529         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39530         this.split.el.addClass("roo-layout-split-h");
39531     }
39532     
39533 };
39534 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39535     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39536     
39537     onRender: function(ctr, pos)
39538     {
39539         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39540         var size = this.config.initialSize || this.config.width;
39541         if(typeof size != "undefined"){
39542             this.el.setWidth(size);
39543         }
39544     },
39545     
39546     getBox : function(){
39547         if(this.collapsed){
39548             return this.collapsedEl.getBox();
39549         }
39550         var box = this.el.getBox();
39551         if (box.width == 0) {
39552             box.width = this.config.width; // kludge?
39553         }
39554         if(this.split){
39555             box.width += this.split.el.getWidth();
39556         }
39557         return box;
39558     },
39559     
39560     updateBox : function(box){
39561         if(this.split && !this.collapsed){
39562             var sw = this.split.el.getWidth();
39563             box.width -= sw;
39564             this.split.el.setLeft(box.x+box.width);
39565             this.split.el.setTop(box.y);
39566             this.split.el.setHeight(box.height);
39567         }
39568         if(this.collapsed){
39569             this.updateBody(null, box.height);
39570         }
39571         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39572     }
39573 });Roo.namespace("Roo.bootstrap.panel");/*
39574  * Based on:
39575  * Ext JS Library 1.1.1
39576  * Copyright(c) 2006-2007, Ext JS, LLC.
39577  *
39578  * Originally Released Under LGPL - original licence link has changed is not relivant.
39579  *
39580  * Fork - LGPL
39581  * <script type="text/javascript">
39582  */
39583 /**
39584  * @class Roo.ContentPanel
39585  * @extends Roo.util.Observable
39586  * A basic ContentPanel element.
39587  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39588  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39589  * @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
39590  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39591  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39592  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39593  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39594  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39595  * @cfg {String} title          The title for this panel
39596  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39597  * @cfg {String} url            Calls {@link #setUrl} with this value
39598  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39599  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39600  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39601  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39602  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39603  * @cfg {Boolean} badges render the badges
39604  * @cfg {String} cls  extra classes to use  
39605  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39606
39607  * @constructor
39608  * Create a new ContentPanel.
39609  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39610  * @param {String/Object} config A string to set only the title or a config object
39611  * @param {String} content (optional) Set the HTML content for this panel
39612  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39613  */
39614 Roo.bootstrap.panel.Content = function( config){
39615     
39616     this.tpl = config.tpl || false;
39617     
39618     var el = config.el;
39619     var content = config.content;
39620
39621     if(config.autoCreate){ // xtype is available if this is called from factory
39622         el = Roo.id();
39623     }
39624     this.el = Roo.get(el);
39625     if(!this.el && config && config.autoCreate){
39626         if(typeof config.autoCreate == "object"){
39627             if(!config.autoCreate.id){
39628                 config.autoCreate.id = config.id||el;
39629             }
39630             this.el = Roo.DomHelper.append(document.body,
39631                         config.autoCreate, true);
39632         }else{
39633             var elcfg =  {
39634                 tag: "div",
39635                 cls: (config.cls || '') +
39636                     (config.background ? ' bg-' + config.background : '') +
39637                     " roo-layout-inactive-content",
39638                 id: config.id||el
39639             };
39640             if (config.iframe) {
39641                 elcfg.cn = [
39642                     {
39643                         tag : 'iframe',
39644                         style : 'border: 0px',
39645                         src : 'about:blank'
39646                     }
39647                 ];
39648             }
39649               
39650             if (config.html) {
39651                 elcfg.html = config.html;
39652                 
39653             }
39654                         
39655             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39656             if (config.iframe) {
39657                 this.iframeEl = this.el.select('iframe',true).first();
39658             }
39659             
39660         }
39661     } 
39662     this.closable = false;
39663     this.loaded = false;
39664     this.active = false;
39665    
39666       
39667     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39668         
39669         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39670         
39671         this.wrapEl = this.el; //this.el.wrap();
39672         var ti = [];
39673         if (config.toolbar.items) {
39674             ti = config.toolbar.items ;
39675             delete config.toolbar.items ;
39676         }
39677         
39678         var nitems = [];
39679         this.toolbar.render(this.wrapEl, 'before');
39680         for(var i =0;i < ti.length;i++) {
39681           //  Roo.log(['add child', items[i]]);
39682             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39683         }
39684         this.toolbar.items = nitems;
39685         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39686         delete config.toolbar;
39687         
39688     }
39689     /*
39690     // xtype created footer. - not sure if will work as we normally have to render first..
39691     if (this.footer && !this.footer.el && this.footer.xtype) {
39692         if (!this.wrapEl) {
39693             this.wrapEl = this.el.wrap();
39694         }
39695     
39696         this.footer.container = this.wrapEl.createChild();
39697          
39698         this.footer = Roo.factory(this.footer, Roo);
39699         
39700     }
39701     */
39702     
39703      if(typeof config == "string"){
39704         this.title = config;
39705     }else{
39706         Roo.apply(this, config);
39707     }
39708     
39709     if(this.resizeEl){
39710         this.resizeEl = Roo.get(this.resizeEl, true);
39711     }else{
39712         this.resizeEl = this.el;
39713     }
39714     // handle view.xtype
39715     
39716  
39717     
39718     
39719     this.addEvents({
39720         /**
39721          * @event activate
39722          * Fires when this panel is activated. 
39723          * @param {Roo.ContentPanel} this
39724          */
39725         "activate" : true,
39726         /**
39727          * @event deactivate
39728          * Fires when this panel is activated. 
39729          * @param {Roo.ContentPanel} this
39730          */
39731         "deactivate" : true,
39732
39733         /**
39734          * @event resize
39735          * Fires when this panel is resized if fitToFrame is true.
39736          * @param {Roo.ContentPanel} this
39737          * @param {Number} width The width after any component adjustments
39738          * @param {Number} height The height after any component adjustments
39739          */
39740         "resize" : true,
39741         
39742          /**
39743          * @event render
39744          * Fires when this tab is created
39745          * @param {Roo.ContentPanel} this
39746          */
39747         "render" : true
39748         
39749         
39750         
39751     });
39752     
39753
39754     
39755     
39756     if(this.autoScroll && !this.iframe){
39757         this.resizeEl.setStyle("overflow", "auto");
39758     } else {
39759         // fix randome scrolling
39760         //this.el.on('scroll', function() {
39761         //    Roo.log('fix random scolling');
39762         //    this.scrollTo('top',0); 
39763         //});
39764     }
39765     content = content || this.content;
39766     if(content){
39767         this.setContent(content);
39768     }
39769     if(config && config.url){
39770         this.setUrl(this.url, this.params, this.loadOnce);
39771     }
39772     
39773     
39774     
39775     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39776     
39777     if (this.view && typeof(this.view.xtype) != 'undefined') {
39778         this.view.el = this.el.appendChild(document.createElement("div"));
39779         this.view = Roo.factory(this.view); 
39780         this.view.render  &&  this.view.render(false, '');  
39781     }
39782     
39783     
39784     this.fireEvent('render', this);
39785 };
39786
39787 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39788     
39789     cls : '',
39790     background : '',
39791     
39792     tabTip : '',
39793     
39794     iframe : false,
39795     iframeEl : false,
39796     
39797     setRegion : function(region){
39798         this.region = region;
39799         this.setActiveClass(region && !this.background);
39800     },
39801     
39802     
39803     setActiveClass: function(state)
39804     {
39805         if(state){
39806            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39807            this.el.setStyle('position','relative');
39808         }else{
39809            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39810            this.el.setStyle('position', 'absolute');
39811         } 
39812     },
39813     
39814     /**
39815      * Returns the toolbar for this Panel if one was configured. 
39816      * @return {Roo.Toolbar} 
39817      */
39818     getToolbar : function(){
39819         return this.toolbar;
39820     },
39821     
39822     setActiveState : function(active)
39823     {
39824         this.active = active;
39825         this.setActiveClass(active);
39826         if(!active){
39827             if(this.fireEvent("deactivate", this) === false){
39828                 return false;
39829             }
39830             return true;
39831         }
39832         this.fireEvent("activate", this);
39833         return true;
39834     },
39835     /**
39836      * Updates this panel's element (not for iframe)
39837      * @param {String} content The new content
39838      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39839     */
39840     setContent : function(content, loadScripts){
39841         if (this.iframe) {
39842             return;
39843         }
39844         
39845         this.el.update(content, loadScripts);
39846     },
39847
39848     ignoreResize : function(w, h){
39849         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39850             return true;
39851         }else{
39852             this.lastSize = {width: w, height: h};
39853             return false;
39854         }
39855     },
39856     /**
39857      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39858      * @return {Roo.UpdateManager} The UpdateManager
39859      */
39860     getUpdateManager : function(){
39861         if (this.iframe) {
39862             return false;
39863         }
39864         return this.el.getUpdateManager();
39865     },
39866      /**
39867      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39868      * Does not work with IFRAME contents
39869      * @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:
39870 <pre><code>
39871 panel.load({
39872     url: "your-url.php",
39873     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39874     callback: yourFunction,
39875     scope: yourObject, //(optional scope)
39876     discardUrl: false,
39877     nocache: false,
39878     text: "Loading...",
39879     timeout: 30,
39880     scripts: false
39881 });
39882 </code></pre>
39883      
39884      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39885      * 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.
39886      * @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}
39887      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39888      * @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.
39889      * @return {Roo.ContentPanel} this
39890      */
39891     load : function(){
39892         
39893         if (this.iframe) {
39894             return this;
39895         }
39896         
39897         var um = this.el.getUpdateManager();
39898         um.update.apply(um, arguments);
39899         return this;
39900     },
39901
39902
39903     /**
39904      * 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.
39905      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39906      * @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)
39907      * @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)
39908      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39909      */
39910     setUrl : function(url, params, loadOnce){
39911         if (this.iframe) {
39912             this.iframeEl.dom.src = url;
39913             return false;
39914         }
39915         
39916         if(this.refreshDelegate){
39917             this.removeListener("activate", this.refreshDelegate);
39918         }
39919         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39920         this.on("activate", this.refreshDelegate);
39921         return this.el.getUpdateManager();
39922     },
39923     
39924     _handleRefresh : function(url, params, loadOnce){
39925         if(!loadOnce || !this.loaded){
39926             var updater = this.el.getUpdateManager();
39927             updater.update(url, params, this._setLoaded.createDelegate(this));
39928         }
39929     },
39930     
39931     _setLoaded : function(){
39932         this.loaded = true;
39933     }, 
39934     
39935     /**
39936      * Returns this panel's id
39937      * @return {String} 
39938      */
39939     getId : function(){
39940         return this.el.id;
39941     },
39942     
39943     /** 
39944      * Returns this panel's element - used by regiosn to add.
39945      * @return {Roo.Element} 
39946      */
39947     getEl : function(){
39948         return this.wrapEl || this.el;
39949     },
39950     
39951    
39952     
39953     adjustForComponents : function(width, height)
39954     {
39955         //Roo.log('adjustForComponents ');
39956         if(this.resizeEl != this.el){
39957             width -= this.el.getFrameWidth('lr');
39958             height -= this.el.getFrameWidth('tb');
39959         }
39960         if(this.toolbar){
39961             var te = this.toolbar.getEl();
39962             te.setWidth(width);
39963             height -= te.getHeight();
39964         }
39965         if(this.footer){
39966             var te = this.footer.getEl();
39967             te.setWidth(width);
39968             height -= te.getHeight();
39969         }
39970         
39971         
39972         if(this.adjustments){
39973             width += this.adjustments[0];
39974             height += this.adjustments[1];
39975         }
39976         return {"width": width, "height": height};
39977     },
39978     
39979     setSize : function(width, height){
39980         if(this.fitToFrame && !this.ignoreResize(width, height)){
39981             if(this.fitContainer && this.resizeEl != this.el){
39982                 this.el.setSize(width, height);
39983             }
39984             var size = this.adjustForComponents(width, height);
39985             if (this.iframe) {
39986                 this.iframeEl.setSize(width,height);
39987             }
39988             
39989             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39990             this.fireEvent('resize', this, size.width, size.height);
39991             
39992             
39993         }
39994     },
39995     
39996     /**
39997      * Returns this panel's title
39998      * @return {String} 
39999      */
40000     getTitle : function(){
40001         
40002         if (typeof(this.title) != 'object') {
40003             return this.title;
40004         }
40005         
40006         var t = '';
40007         for (var k in this.title) {
40008             if (!this.title.hasOwnProperty(k)) {
40009                 continue;
40010             }
40011             
40012             if (k.indexOf('-') >= 0) {
40013                 var s = k.split('-');
40014                 for (var i = 0; i<s.length; i++) {
40015                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40016                 }
40017             } else {
40018                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40019             }
40020         }
40021         return t;
40022     },
40023     
40024     /**
40025      * Set this panel's title
40026      * @param {String} title
40027      */
40028     setTitle : function(title){
40029         this.title = title;
40030         if(this.region){
40031             this.region.updatePanelTitle(this, title);
40032         }
40033     },
40034     
40035     /**
40036      * Returns true is this panel was configured to be closable
40037      * @return {Boolean} 
40038      */
40039     isClosable : function(){
40040         return this.closable;
40041     },
40042     
40043     beforeSlide : function(){
40044         this.el.clip();
40045         this.resizeEl.clip();
40046     },
40047     
40048     afterSlide : function(){
40049         this.el.unclip();
40050         this.resizeEl.unclip();
40051     },
40052     
40053     /**
40054      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40055      *   Will fail silently if the {@link #setUrl} method has not been called.
40056      *   This does not activate the panel, just updates its content.
40057      */
40058     refresh : function(){
40059         if(this.refreshDelegate){
40060            this.loaded = false;
40061            this.refreshDelegate();
40062         }
40063     },
40064     
40065     /**
40066      * Destroys this panel
40067      */
40068     destroy : function(){
40069         this.el.removeAllListeners();
40070         var tempEl = document.createElement("span");
40071         tempEl.appendChild(this.el.dom);
40072         tempEl.innerHTML = "";
40073         this.el.remove();
40074         this.el = null;
40075     },
40076     
40077     /**
40078      * form - if the content panel contains a form - this is a reference to it.
40079      * @type {Roo.form.Form}
40080      */
40081     form : false,
40082     /**
40083      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40084      *    This contains a reference to it.
40085      * @type {Roo.View}
40086      */
40087     view : false,
40088     
40089       /**
40090      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40091      * <pre><code>
40092
40093 layout.addxtype({
40094        xtype : 'Form',
40095        items: [ .... ]
40096    }
40097 );
40098
40099 </code></pre>
40100      * @param {Object} cfg Xtype definition of item to add.
40101      */
40102     
40103     
40104     getChildContainer: function () {
40105         return this.getEl();
40106     }
40107     
40108     
40109     /*
40110         var  ret = new Roo.factory(cfg);
40111         return ret;
40112         
40113         
40114         // add form..
40115         if (cfg.xtype.match(/^Form$/)) {
40116             
40117             var el;
40118             //if (this.footer) {
40119             //    el = this.footer.container.insertSibling(false, 'before');
40120             //} else {
40121                 el = this.el.createChild();
40122             //}
40123
40124             this.form = new  Roo.form.Form(cfg);
40125             
40126             
40127             if ( this.form.allItems.length) {
40128                 this.form.render(el.dom);
40129             }
40130             return this.form;
40131         }
40132         // should only have one of theses..
40133         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40134             // views.. should not be just added - used named prop 'view''
40135             
40136             cfg.el = this.el.appendChild(document.createElement("div"));
40137             // factory?
40138             
40139             var ret = new Roo.factory(cfg);
40140              
40141              ret.render && ret.render(false, ''); // render blank..
40142             this.view = ret;
40143             return ret;
40144         }
40145         return false;
40146     }
40147     \*/
40148 });
40149  
40150 /**
40151  * @class Roo.bootstrap.panel.Grid
40152  * @extends Roo.bootstrap.panel.Content
40153  * @constructor
40154  * Create a new GridPanel.
40155  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40156  * @param {Object} config A the config object
40157   
40158  */
40159
40160
40161
40162 Roo.bootstrap.panel.Grid = function(config)
40163 {
40164     
40165       
40166     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40167         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40168
40169     config.el = this.wrapper;
40170     //this.el = this.wrapper;
40171     
40172       if (config.container) {
40173         // ctor'ed from a Border/panel.grid
40174         
40175         
40176         this.wrapper.setStyle("overflow", "hidden");
40177         this.wrapper.addClass('roo-grid-container');
40178
40179     }
40180     
40181     
40182     if(config.toolbar){
40183         var tool_el = this.wrapper.createChild();    
40184         this.toolbar = Roo.factory(config.toolbar);
40185         var ti = [];
40186         if (config.toolbar.items) {
40187             ti = config.toolbar.items ;
40188             delete config.toolbar.items ;
40189         }
40190         
40191         var nitems = [];
40192         this.toolbar.render(tool_el);
40193         for(var i =0;i < ti.length;i++) {
40194           //  Roo.log(['add child', items[i]]);
40195             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40196         }
40197         this.toolbar.items = nitems;
40198         
40199         delete config.toolbar;
40200     }
40201     
40202     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40203     config.grid.scrollBody = true;;
40204     config.grid.monitorWindowResize = false; // turn off autosizing
40205     config.grid.autoHeight = false;
40206     config.grid.autoWidth = false;
40207     
40208     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40209     
40210     if (config.background) {
40211         // render grid on panel activation (if panel background)
40212         this.on('activate', function(gp) {
40213             if (!gp.grid.rendered) {
40214                 gp.grid.render(this.wrapper);
40215                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40216             }
40217         });
40218             
40219     } else {
40220         this.grid.render(this.wrapper);
40221         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40222
40223     }
40224     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40225     // ??? needed ??? config.el = this.wrapper;
40226     
40227     
40228     
40229   
40230     // xtype created footer. - not sure if will work as we normally have to render first..
40231     if (this.footer && !this.footer.el && this.footer.xtype) {
40232         
40233         var ctr = this.grid.getView().getFooterPanel(true);
40234         this.footer.dataSource = this.grid.dataSource;
40235         this.footer = Roo.factory(this.footer, Roo);
40236         this.footer.render(ctr);
40237         
40238     }
40239     
40240     
40241     
40242     
40243      
40244 };
40245
40246 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40247     getId : function(){
40248         return this.grid.id;
40249     },
40250     
40251     /**
40252      * Returns the grid for this panel
40253      * @return {Roo.bootstrap.Table} 
40254      */
40255     getGrid : function(){
40256         return this.grid;    
40257     },
40258     
40259     setSize : function(width, height){
40260         if(!this.ignoreResize(width, height)){
40261             var grid = this.grid;
40262             var size = this.adjustForComponents(width, height);
40263             // tfoot is not a footer?
40264           
40265             
40266             var gridel = grid.getGridEl();
40267             gridel.setSize(size.width, size.height);
40268             
40269             var tbd = grid.getGridEl().select('tbody', true).first();
40270             var thd = grid.getGridEl().select('thead',true).first();
40271             var tbf= grid.getGridEl().select('tfoot', true).first();
40272
40273             if (tbf) {
40274                 size.height -= tbf.getHeight();
40275             }
40276             if (thd) {
40277                 size.height -= thd.getHeight();
40278             }
40279             
40280             tbd.setSize(size.width, size.height );
40281             // this is for the account management tab -seems to work there.
40282             var thd = grid.getGridEl().select('thead',true).first();
40283             //if (tbd) {
40284             //    tbd.setSize(size.width, size.height - thd.getHeight());
40285             //}
40286              
40287             grid.autoSize();
40288         }
40289     },
40290      
40291     
40292     
40293     beforeSlide : function(){
40294         this.grid.getView().scroller.clip();
40295     },
40296     
40297     afterSlide : function(){
40298         this.grid.getView().scroller.unclip();
40299     },
40300     
40301     destroy : function(){
40302         this.grid.destroy();
40303         delete this.grid;
40304         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40305     }
40306 });
40307
40308 /**
40309  * @class Roo.bootstrap.panel.Nest
40310  * @extends Roo.bootstrap.panel.Content
40311  * @constructor
40312  * Create a new Panel, that can contain a layout.Border.
40313  * 
40314  * 
40315  * @param {Roo.BorderLayout} layout The layout for this panel
40316  * @param {String/Object} config A string to set only the title or a config object
40317  */
40318 Roo.bootstrap.panel.Nest = function(config)
40319 {
40320     // construct with only one argument..
40321     /* FIXME - implement nicer consturctors
40322     if (layout.layout) {
40323         config = layout;
40324         layout = config.layout;
40325         delete config.layout;
40326     }
40327     if (layout.xtype && !layout.getEl) {
40328         // then layout needs constructing..
40329         layout = Roo.factory(layout, Roo);
40330     }
40331     */
40332     
40333     config.el =  config.layout.getEl();
40334     
40335     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40336     
40337     config.layout.monitorWindowResize = false; // turn off autosizing
40338     this.layout = config.layout;
40339     this.layout.getEl().addClass("roo-layout-nested-layout");
40340     this.layout.parent = this;
40341     
40342     
40343     
40344     
40345 };
40346
40347 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40348
40349     setSize : function(width, height){
40350         if(!this.ignoreResize(width, height)){
40351             var size = this.adjustForComponents(width, height);
40352             var el = this.layout.getEl();
40353             if (size.height < 1) {
40354                 el.setWidth(size.width);   
40355             } else {
40356                 el.setSize(size.width, size.height);
40357             }
40358             var touch = el.dom.offsetWidth;
40359             this.layout.layout();
40360             // ie requires a double layout on the first pass
40361             if(Roo.isIE && !this.initialized){
40362                 this.initialized = true;
40363                 this.layout.layout();
40364             }
40365         }
40366     },
40367     
40368     // activate all subpanels if not currently active..
40369     
40370     setActiveState : function(active){
40371         this.active = active;
40372         this.setActiveClass(active);
40373         
40374         if(!active){
40375             this.fireEvent("deactivate", this);
40376             return;
40377         }
40378         
40379         this.fireEvent("activate", this);
40380         // not sure if this should happen before or after..
40381         if (!this.layout) {
40382             return; // should not happen..
40383         }
40384         var reg = false;
40385         for (var r in this.layout.regions) {
40386             reg = this.layout.getRegion(r);
40387             if (reg.getActivePanel()) {
40388                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40389                 reg.setActivePanel(reg.getActivePanel());
40390                 continue;
40391             }
40392             if (!reg.panels.length) {
40393                 continue;
40394             }
40395             reg.showPanel(reg.getPanel(0));
40396         }
40397         
40398         
40399         
40400         
40401     },
40402     
40403     /**
40404      * Returns the nested BorderLayout for this panel
40405      * @return {Roo.BorderLayout} 
40406      */
40407     getLayout : function(){
40408         return this.layout;
40409     },
40410     
40411      /**
40412      * Adds a xtype elements to the layout of the nested panel
40413      * <pre><code>
40414
40415 panel.addxtype({
40416        xtype : 'ContentPanel',
40417        region: 'west',
40418        items: [ .... ]
40419    }
40420 );
40421
40422 panel.addxtype({
40423         xtype : 'NestedLayoutPanel',
40424         region: 'west',
40425         layout: {
40426            center: { },
40427            west: { }   
40428         },
40429         items : [ ... list of content panels or nested layout panels.. ]
40430    }
40431 );
40432 </code></pre>
40433      * @param {Object} cfg Xtype definition of item to add.
40434      */
40435     addxtype : function(cfg) {
40436         return this.layout.addxtype(cfg);
40437     
40438     }
40439 });/*
40440  * Based on:
40441  * Ext JS Library 1.1.1
40442  * Copyright(c) 2006-2007, Ext JS, LLC.
40443  *
40444  * Originally Released Under LGPL - original licence link has changed is not relivant.
40445  *
40446  * Fork - LGPL
40447  * <script type="text/javascript">
40448  */
40449 /**
40450  * @class Roo.TabPanel
40451  * @extends Roo.util.Observable
40452  * A lightweight tab container.
40453  * <br><br>
40454  * Usage:
40455  * <pre><code>
40456 // basic tabs 1, built from existing content
40457 var tabs = new Roo.TabPanel("tabs1");
40458 tabs.addTab("script", "View Script");
40459 tabs.addTab("markup", "View Markup");
40460 tabs.activate("script");
40461
40462 // more advanced tabs, built from javascript
40463 var jtabs = new Roo.TabPanel("jtabs");
40464 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40465
40466 // set up the UpdateManager
40467 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40468 var updater = tab2.getUpdateManager();
40469 updater.setDefaultUrl("ajax1.htm");
40470 tab2.on('activate', updater.refresh, updater, true);
40471
40472 // Use setUrl for Ajax loading
40473 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40474 tab3.setUrl("ajax2.htm", null, true);
40475
40476 // Disabled tab
40477 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40478 tab4.disable();
40479
40480 jtabs.activate("jtabs-1");
40481  * </code></pre>
40482  * @constructor
40483  * Create a new TabPanel.
40484  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40485  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40486  */
40487 Roo.bootstrap.panel.Tabs = function(config){
40488     /**
40489     * The container element for this TabPanel.
40490     * @type Roo.Element
40491     */
40492     this.el = Roo.get(config.el);
40493     delete config.el;
40494     if(config){
40495         if(typeof config == "boolean"){
40496             this.tabPosition = config ? "bottom" : "top";
40497         }else{
40498             Roo.apply(this, config);
40499         }
40500     }
40501     
40502     if(this.tabPosition == "bottom"){
40503         // if tabs are at the bottom = create the body first.
40504         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40505         this.el.addClass("roo-tabs-bottom");
40506     }
40507     // next create the tabs holders
40508     
40509     if (this.tabPosition == "west"){
40510         
40511         var reg = this.region; // fake it..
40512         while (reg) {
40513             if (!reg.mgr.parent) {
40514                 break;
40515             }
40516             reg = reg.mgr.parent.region;
40517         }
40518         Roo.log("got nest?");
40519         Roo.log(reg);
40520         if (reg.mgr.getRegion('west')) {
40521             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40522             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40523             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40524             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40525             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40526         
40527             
40528         }
40529         
40530         
40531     } else {
40532      
40533         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40534         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40535         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40536         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40537     }
40538     
40539     
40540     if(Roo.isIE){
40541         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40542     }
40543     
40544     // finally - if tabs are at the top, then create the body last..
40545     if(this.tabPosition != "bottom"){
40546         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40547          * @type Roo.Element
40548          */
40549         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40550         this.el.addClass("roo-tabs-top");
40551     }
40552     this.items = [];
40553
40554     this.bodyEl.setStyle("position", "relative");
40555
40556     this.active = null;
40557     this.activateDelegate = this.activate.createDelegate(this);
40558
40559     this.addEvents({
40560         /**
40561          * @event tabchange
40562          * Fires when the active tab changes
40563          * @param {Roo.TabPanel} this
40564          * @param {Roo.TabPanelItem} activePanel The new active tab
40565          */
40566         "tabchange": true,
40567         /**
40568          * @event beforetabchange
40569          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40570          * @param {Roo.TabPanel} this
40571          * @param {Object} e Set cancel to true on this object to cancel the tab change
40572          * @param {Roo.TabPanelItem} tab The tab being changed to
40573          */
40574         "beforetabchange" : true
40575     });
40576
40577     Roo.EventManager.onWindowResize(this.onResize, this);
40578     this.cpad = this.el.getPadding("lr");
40579     this.hiddenCount = 0;
40580
40581
40582     // toolbar on the tabbar support...
40583     if (this.toolbar) {
40584         alert("no toolbar support yet");
40585         this.toolbar  = false;
40586         /*
40587         var tcfg = this.toolbar;
40588         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40589         this.toolbar = new Roo.Toolbar(tcfg);
40590         if (Roo.isSafari) {
40591             var tbl = tcfg.container.child('table', true);
40592             tbl.setAttribute('width', '100%');
40593         }
40594         */
40595         
40596     }
40597    
40598
40599
40600     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40601 };
40602
40603 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40604     /*
40605      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40606      */
40607     tabPosition : "top",
40608     /*
40609      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40610      */
40611     currentTabWidth : 0,
40612     /*
40613      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40614      */
40615     minTabWidth : 40,
40616     /*
40617      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40618      */
40619     maxTabWidth : 250,
40620     /*
40621      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40622      */
40623     preferredTabWidth : 175,
40624     /*
40625      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40626      */
40627     resizeTabs : false,
40628     /*
40629      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40630      */
40631     monitorResize : true,
40632     /*
40633      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40634      */
40635     toolbar : false,  // set by caller..
40636     
40637     region : false, /// set by caller
40638     
40639     disableTooltips : true, // not used yet...
40640
40641     /**
40642      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40643      * @param {String} id The id of the div to use <b>or create</b>
40644      * @param {String} text The text for the tab
40645      * @param {String} content (optional) Content to put in the TabPanelItem body
40646      * @param {Boolean} closable (optional) True to create a close icon on the tab
40647      * @return {Roo.TabPanelItem} The created TabPanelItem
40648      */
40649     addTab : function(id, text, content, closable, tpl)
40650     {
40651         var item = new Roo.bootstrap.panel.TabItem({
40652             panel: this,
40653             id : id,
40654             text : text,
40655             closable : closable,
40656             tpl : tpl
40657         });
40658         this.addTabItem(item);
40659         if(content){
40660             item.setContent(content);
40661         }
40662         return item;
40663     },
40664
40665     /**
40666      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40667      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40668      * @return {Roo.TabPanelItem}
40669      */
40670     getTab : function(id){
40671         return this.items[id];
40672     },
40673
40674     /**
40675      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40676      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40677      */
40678     hideTab : function(id){
40679         var t = this.items[id];
40680         if(!t.isHidden()){
40681            t.setHidden(true);
40682            this.hiddenCount++;
40683            this.autoSizeTabs();
40684         }
40685     },
40686
40687     /**
40688      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40689      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40690      */
40691     unhideTab : function(id){
40692         var t = this.items[id];
40693         if(t.isHidden()){
40694            t.setHidden(false);
40695            this.hiddenCount--;
40696            this.autoSizeTabs();
40697         }
40698     },
40699
40700     /**
40701      * Adds an existing {@link Roo.TabPanelItem}.
40702      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40703      */
40704     addTabItem : function(item)
40705     {
40706         this.items[item.id] = item;
40707         this.items.push(item);
40708         this.autoSizeTabs();
40709       //  if(this.resizeTabs){
40710     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40711   //         this.autoSizeTabs();
40712 //        }else{
40713 //            item.autoSize();
40714        // }
40715     },
40716
40717     /**
40718      * Removes a {@link Roo.TabPanelItem}.
40719      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40720      */
40721     removeTab : function(id){
40722         var items = this.items;
40723         var tab = items[id];
40724         if(!tab) { return; }
40725         var index = items.indexOf(tab);
40726         if(this.active == tab && items.length > 1){
40727             var newTab = this.getNextAvailable(index);
40728             if(newTab) {
40729                 newTab.activate();
40730             }
40731         }
40732         this.stripEl.dom.removeChild(tab.pnode.dom);
40733         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40734             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40735         }
40736         items.splice(index, 1);
40737         delete this.items[tab.id];
40738         tab.fireEvent("close", tab);
40739         tab.purgeListeners();
40740         this.autoSizeTabs();
40741     },
40742
40743     getNextAvailable : function(start){
40744         var items = this.items;
40745         var index = start;
40746         // look for a next tab that will slide over to
40747         // replace the one being removed
40748         while(index < items.length){
40749             var item = items[++index];
40750             if(item && !item.isHidden()){
40751                 return item;
40752             }
40753         }
40754         // if one isn't found select the previous tab (on the left)
40755         index = start;
40756         while(index >= 0){
40757             var item = items[--index];
40758             if(item && !item.isHidden()){
40759                 return item;
40760             }
40761         }
40762         return null;
40763     },
40764
40765     /**
40766      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40767      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40768      */
40769     disableTab : function(id){
40770         var tab = this.items[id];
40771         if(tab && this.active != tab){
40772             tab.disable();
40773         }
40774     },
40775
40776     /**
40777      * Enables a {@link Roo.TabPanelItem} that is disabled.
40778      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40779      */
40780     enableTab : function(id){
40781         var tab = this.items[id];
40782         tab.enable();
40783     },
40784
40785     /**
40786      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40787      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40788      * @return {Roo.TabPanelItem} The TabPanelItem.
40789      */
40790     activate : function(id)
40791     {
40792         //Roo.log('activite:'  + id);
40793         
40794         var tab = this.items[id];
40795         if(!tab){
40796             return null;
40797         }
40798         if(tab == this.active || tab.disabled){
40799             return tab;
40800         }
40801         var e = {};
40802         this.fireEvent("beforetabchange", this, e, tab);
40803         if(e.cancel !== true && !tab.disabled){
40804             if(this.active){
40805                 this.active.hide();
40806             }
40807             this.active = this.items[id];
40808             this.active.show();
40809             this.fireEvent("tabchange", this, this.active);
40810         }
40811         return tab;
40812     },
40813
40814     /**
40815      * Gets the active {@link Roo.TabPanelItem}.
40816      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40817      */
40818     getActiveTab : function(){
40819         return this.active;
40820     },
40821
40822     /**
40823      * Updates the tab body element to fit the height of the container element
40824      * for overflow scrolling
40825      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40826      */
40827     syncHeight : function(targetHeight){
40828         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40829         var bm = this.bodyEl.getMargins();
40830         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40831         this.bodyEl.setHeight(newHeight);
40832         return newHeight;
40833     },
40834
40835     onResize : function(){
40836         if(this.monitorResize){
40837             this.autoSizeTabs();
40838         }
40839     },
40840
40841     /**
40842      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40843      */
40844     beginUpdate : function(){
40845         this.updating = true;
40846     },
40847
40848     /**
40849      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40850      */
40851     endUpdate : function(){
40852         this.updating = false;
40853         this.autoSizeTabs();
40854     },
40855
40856     /**
40857      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40858      */
40859     autoSizeTabs : function()
40860     {
40861         var count = this.items.length;
40862         var vcount = count - this.hiddenCount;
40863         
40864         if (vcount < 2) {
40865             this.stripEl.hide();
40866         } else {
40867             this.stripEl.show();
40868         }
40869         
40870         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40871             return;
40872         }
40873         
40874         
40875         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40876         var availWidth = Math.floor(w / vcount);
40877         var b = this.stripBody;
40878         if(b.getWidth() > w){
40879             var tabs = this.items;
40880             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40881             if(availWidth < this.minTabWidth){
40882                 /*if(!this.sleft){    // incomplete scrolling code
40883                     this.createScrollButtons();
40884                 }
40885                 this.showScroll();
40886                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40887             }
40888         }else{
40889             if(this.currentTabWidth < this.preferredTabWidth){
40890                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40891             }
40892         }
40893     },
40894
40895     /**
40896      * Returns the number of tabs in this TabPanel.
40897      * @return {Number}
40898      */
40899      getCount : function(){
40900          return this.items.length;
40901      },
40902
40903     /**
40904      * Resizes all the tabs to the passed width
40905      * @param {Number} The new width
40906      */
40907     setTabWidth : function(width){
40908         this.currentTabWidth = width;
40909         for(var i = 0, len = this.items.length; i < len; i++) {
40910                 if(!this.items[i].isHidden()) {
40911                 this.items[i].setWidth(width);
40912             }
40913         }
40914     },
40915
40916     /**
40917      * Destroys this TabPanel
40918      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40919      */
40920     destroy : function(removeEl){
40921         Roo.EventManager.removeResizeListener(this.onResize, this);
40922         for(var i = 0, len = this.items.length; i < len; i++){
40923             this.items[i].purgeListeners();
40924         }
40925         if(removeEl === true){
40926             this.el.update("");
40927             this.el.remove();
40928         }
40929     },
40930     
40931     createStrip : function(container)
40932     {
40933         var strip = document.createElement("nav");
40934         strip.className = Roo.bootstrap.version == 4 ?
40935             "navbar-light bg-light" : 
40936             "navbar navbar-default"; //"x-tabs-wrap";
40937         container.appendChild(strip);
40938         return strip;
40939     },
40940     
40941     createStripList : function(strip)
40942     {
40943         // div wrapper for retard IE
40944         // returns the "tr" element.
40945         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40946         //'<div class="x-tabs-strip-wrap">'+
40947           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40948           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40949         return strip.firstChild; //.firstChild.firstChild.firstChild;
40950     },
40951     createBody : function(container)
40952     {
40953         var body = document.createElement("div");
40954         Roo.id(body, "tab-body");
40955         //Roo.fly(body).addClass("x-tabs-body");
40956         Roo.fly(body).addClass("tab-content");
40957         container.appendChild(body);
40958         return body;
40959     },
40960     createItemBody :function(bodyEl, id){
40961         var body = Roo.getDom(id);
40962         if(!body){
40963             body = document.createElement("div");
40964             body.id = id;
40965         }
40966         //Roo.fly(body).addClass("x-tabs-item-body");
40967         Roo.fly(body).addClass("tab-pane");
40968          bodyEl.insertBefore(body, bodyEl.firstChild);
40969         return body;
40970     },
40971     /** @private */
40972     createStripElements :  function(stripEl, text, closable, tpl)
40973     {
40974         var td = document.createElement("li"); // was td..
40975         td.className = 'nav-item';
40976         
40977         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40978         
40979         
40980         stripEl.appendChild(td);
40981         /*if(closable){
40982             td.className = "x-tabs-closable";
40983             if(!this.closeTpl){
40984                 this.closeTpl = new Roo.Template(
40985                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40986                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40987                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40988                 );
40989             }
40990             var el = this.closeTpl.overwrite(td, {"text": text});
40991             var close = el.getElementsByTagName("div")[0];
40992             var inner = el.getElementsByTagName("em")[0];
40993             return {"el": el, "close": close, "inner": inner};
40994         } else {
40995         */
40996         // not sure what this is..
40997 //            if(!this.tabTpl){
40998                 //this.tabTpl = new Roo.Template(
40999                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41000                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41001                 //);
41002 //                this.tabTpl = new Roo.Template(
41003 //                   '<a href="#">' +
41004 //                   '<span unselectable="on"' +
41005 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41006 //                            ' >{text}</span></a>'
41007 //                );
41008 //                
41009 //            }
41010
41011
41012             var template = tpl || this.tabTpl || false;
41013             
41014             if(!template){
41015                 template =  new Roo.Template(
41016                         Roo.bootstrap.version == 4 ? 
41017                             (
41018                                 '<a class="nav-link" href="#" unselectable="on"' +
41019                                      (this.disableTooltips ? '' : ' title="{text}"') +
41020                                      ' >{text}</a>'
41021                             ) : (
41022                                 '<a class="nav-link" href="#">' +
41023                                 '<span unselectable="on"' +
41024                                          (this.disableTooltips ? '' : ' title="{text}"') +
41025                                     ' >{text}</span></a>'
41026                             )
41027                 );
41028             }
41029             
41030             switch (typeof(template)) {
41031                 case 'object' :
41032                     break;
41033                 case 'string' :
41034                     template = new Roo.Template(template);
41035                     break;
41036                 default :
41037                     break;
41038             }
41039             
41040             var el = template.overwrite(td, {"text": text});
41041             
41042             var inner = el.getElementsByTagName("span")[0];
41043             
41044             return {"el": el, "inner": inner};
41045             
41046     }
41047         
41048     
41049 });
41050
41051 /**
41052  * @class Roo.TabPanelItem
41053  * @extends Roo.util.Observable
41054  * Represents an individual item (tab plus body) in a TabPanel.
41055  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41056  * @param {String} id The id of this TabPanelItem
41057  * @param {String} text The text for the tab of this TabPanelItem
41058  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41059  */
41060 Roo.bootstrap.panel.TabItem = function(config){
41061     /**
41062      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41063      * @type Roo.TabPanel
41064      */
41065     this.tabPanel = config.panel;
41066     /**
41067      * The id for this TabPanelItem
41068      * @type String
41069      */
41070     this.id = config.id;
41071     /** @private */
41072     this.disabled = false;
41073     /** @private */
41074     this.text = config.text;
41075     /** @private */
41076     this.loaded = false;
41077     this.closable = config.closable;
41078
41079     /**
41080      * The body element for this TabPanelItem.
41081      * @type Roo.Element
41082      */
41083     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41084     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41085     this.bodyEl.setStyle("display", "block");
41086     this.bodyEl.setStyle("zoom", "1");
41087     //this.hideAction();
41088
41089     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41090     /** @private */
41091     this.el = Roo.get(els.el);
41092     this.inner = Roo.get(els.inner, true);
41093      this.textEl = Roo.bootstrap.version == 4 ?
41094         this.el : Roo.get(this.el.dom.firstChild, true);
41095
41096     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41097     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41098
41099     
41100 //    this.el.on("mousedown", this.onTabMouseDown, this);
41101     this.el.on("click", this.onTabClick, this);
41102     /** @private */
41103     if(config.closable){
41104         var c = Roo.get(els.close, true);
41105         c.dom.title = this.closeText;
41106         c.addClassOnOver("close-over");
41107         c.on("click", this.closeClick, this);
41108      }
41109
41110     this.addEvents({
41111          /**
41112          * @event activate
41113          * Fires when this tab becomes the active tab.
41114          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41115          * @param {Roo.TabPanelItem} this
41116          */
41117         "activate": true,
41118         /**
41119          * @event beforeclose
41120          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41121          * @param {Roo.TabPanelItem} this
41122          * @param {Object} e Set cancel to true on this object to cancel the close.
41123          */
41124         "beforeclose": true,
41125         /**
41126          * @event close
41127          * Fires when this tab is closed.
41128          * @param {Roo.TabPanelItem} this
41129          */
41130          "close": true,
41131         /**
41132          * @event deactivate
41133          * Fires when this tab is no longer the active tab.
41134          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41135          * @param {Roo.TabPanelItem} this
41136          */
41137          "deactivate" : true
41138     });
41139     this.hidden = false;
41140
41141     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41142 };
41143
41144 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41145            {
41146     purgeListeners : function(){
41147        Roo.util.Observable.prototype.purgeListeners.call(this);
41148        this.el.removeAllListeners();
41149     },
41150     /**
41151      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41152      */
41153     show : function(){
41154         this.status_node.addClass("active");
41155         this.showAction();
41156         if(Roo.isOpera){
41157             this.tabPanel.stripWrap.repaint();
41158         }
41159         this.fireEvent("activate", this.tabPanel, this);
41160     },
41161
41162     /**
41163      * Returns true if this tab is the active tab.
41164      * @return {Boolean}
41165      */
41166     isActive : function(){
41167         return this.tabPanel.getActiveTab() == this;
41168     },
41169
41170     /**
41171      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41172      */
41173     hide : function(){
41174         this.status_node.removeClass("active");
41175         this.hideAction();
41176         this.fireEvent("deactivate", this.tabPanel, this);
41177     },
41178
41179     hideAction : function(){
41180         this.bodyEl.hide();
41181         this.bodyEl.setStyle("position", "absolute");
41182         this.bodyEl.setLeft("-20000px");
41183         this.bodyEl.setTop("-20000px");
41184     },
41185
41186     showAction : function(){
41187         this.bodyEl.setStyle("position", "relative");
41188         this.bodyEl.setTop("");
41189         this.bodyEl.setLeft("");
41190         this.bodyEl.show();
41191     },
41192
41193     /**
41194      * Set the tooltip for the tab.
41195      * @param {String} tooltip The tab's tooltip
41196      */
41197     setTooltip : function(text){
41198         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41199             this.textEl.dom.qtip = text;
41200             this.textEl.dom.removeAttribute('title');
41201         }else{
41202             this.textEl.dom.title = text;
41203         }
41204     },
41205
41206     onTabClick : function(e){
41207         e.preventDefault();
41208         this.tabPanel.activate(this.id);
41209     },
41210
41211     onTabMouseDown : function(e){
41212         e.preventDefault();
41213         this.tabPanel.activate(this.id);
41214     },
41215 /*
41216     getWidth : function(){
41217         return this.inner.getWidth();
41218     },
41219
41220     setWidth : function(width){
41221         var iwidth = width - this.linode.getPadding("lr");
41222         this.inner.setWidth(iwidth);
41223         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41224         this.linode.setWidth(width);
41225     },
41226 */
41227     /**
41228      * Show or hide the tab
41229      * @param {Boolean} hidden True to hide or false to show.
41230      */
41231     setHidden : function(hidden){
41232         this.hidden = hidden;
41233         this.linode.setStyle("display", hidden ? "none" : "");
41234     },
41235
41236     /**
41237      * Returns true if this tab is "hidden"
41238      * @return {Boolean}
41239      */
41240     isHidden : function(){
41241         return this.hidden;
41242     },
41243
41244     /**
41245      * Returns the text for this tab
41246      * @return {String}
41247      */
41248     getText : function(){
41249         return this.text;
41250     },
41251     /*
41252     autoSize : function(){
41253         //this.el.beginMeasure();
41254         this.textEl.setWidth(1);
41255         /*
41256          *  #2804 [new] Tabs in Roojs
41257          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41258          */
41259         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41260         //this.el.endMeasure();
41261     //},
41262
41263     /**
41264      * Sets the text for the tab (Note: this also sets the tooltip text)
41265      * @param {String} text The tab's text and tooltip
41266      */
41267     setText : function(text){
41268         this.text = text;
41269         this.textEl.update(text);
41270         this.setTooltip(text);
41271         //if(!this.tabPanel.resizeTabs){
41272         //    this.autoSize();
41273         //}
41274     },
41275     /**
41276      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41277      */
41278     activate : function(){
41279         this.tabPanel.activate(this.id);
41280     },
41281
41282     /**
41283      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41284      */
41285     disable : function(){
41286         if(this.tabPanel.active != this){
41287             this.disabled = true;
41288             this.status_node.addClass("disabled");
41289         }
41290     },
41291
41292     /**
41293      * Enables this TabPanelItem if it was previously disabled.
41294      */
41295     enable : function(){
41296         this.disabled = false;
41297         this.status_node.removeClass("disabled");
41298     },
41299
41300     /**
41301      * Sets the content for this TabPanelItem.
41302      * @param {String} content The content
41303      * @param {Boolean} loadScripts true to look for and load scripts
41304      */
41305     setContent : function(content, loadScripts){
41306         this.bodyEl.update(content, loadScripts);
41307     },
41308
41309     /**
41310      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41311      * @return {Roo.UpdateManager} The UpdateManager
41312      */
41313     getUpdateManager : function(){
41314         return this.bodyEl.getUpdateManager();
41315     },
41316
41317     /**
41318      * Set a URL to be used to load the content for this TabPanelItem.
41319      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41320      * @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)
41321      * @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)
41322      * @return {Roo.UpdateManager} The UpdateManager
41323      */
41324     setUrl : function(url, params, loadOnce){
41325         if(this.refreshDelegate){
41326             this.un('activate', this.refreshDelegate);
41327         }
41328         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41329         this.on("activate", this.refreshDelegate);
41330         return this.bodyEl.getUpdateManager();
41331     },
41332
41333     /** @private */
41334     _handleRefresh : function(url, params, loadOnce){
41335         if(!loadOnce || !this.loaded){
41336             var updater = this.bodyEl.getUpdateManager();
41337             updater.update(url, params, this._setLoaded.createDelegate(this));
41338         }
41339     },
41340
41341     /**
41342      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41343      *   Will fail silently if the setUrl method has not been called.
41344      *   This does not activate the panel, just updates its content.
41345      */
41346     refresh : function(){
41347         if(this.refreshDelegate){
41348            this.loaded = false;
41349            this.refreshDelegate();
41350         }
41351     },
41352
41353     /** @private */
41354     _setLoaded : function(){
41355         this.loaded = true;
41356     },
41357
41358     /** @private */
41359     closeClick : function(e){
41360         var o = {};
41361         e.stopEvent();
41362         this.fireEvent("beforeclose", this, o);
41363         if(o.cancel !== true){
41364             this.tabPanel.removeTab(this.id);
41365         }
41366     },
41367     /**
41368      * The text displayed in the tooltip for the close icon.
41369      * @type String
41370      */
41371     closeText : "Close this tab"
41372 });
41373 /**
41374 *    This script refer to:
41375 *    Title: International Telephone Input
41376 *    Author: Jack O'Connor
41377 *    Code version:  v12.1.12
41378 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41379 **/
41380
41381 Roo.bootstrap.PhoneInputData = function() {
41382     var d = [
41383       [
41384         "Afghanistan (‫افغانستان‬‎)",
41385         "af",
41386         "93"
41387       ],
41388       [
41389         "Albania (Shqipëri)",
41390         "al",
41391         "355"
41392       ],
41393       [
41394         "Algeria (‫الجزائر‬‎)",
41395         "dz",
41396         "213"
41397       ],
41398       [
41399         "American Samoa",
41400         "as",
41401         "1684"
41402       ],
41403       [
41404         "Andorra",
41405         "ad",
41406         "376"
41407       ],
41408       [
41409         "Angola",
41410         "ao",
41411         "244"
41412       ],
41413       [
41414         "Anguilla",
41415         "ai",
41416         "1264"
41417       ],
41418       [
41419         "Antigua and Barbuda",
41420         "ag",
41421         "1268"
41422       ],
41423       [
41424         "Argentina",
41425         "ar",
41426         "54"
41427       ],
41428       [
41429         "Armenia (Հայաստան)",
41430         "am",
41431         "374"
41432       ],
41433       [
41434         "Aruba",
41435         "aw",
41436         "297"
41437       ],
41438       [
41439         "Australia",
41440         "au",
41441         "61",
41442         0
41443       ],
41444       [
41445         "Austria (Österreich)",
41446         "at",
41447         "43"
41448       ],
41449       [
41450         "Azerbaijan (Azərbaycan)",
41451         "az",
41452         "994"
41453       ],
41454       [
41455         "Bahamas",
41456         "bs",
41457         "1242"
41458       ],
41459       [
41460         "Bahrain (‫البحرين‬‎)",
41461         "bh",
41462         "973"
41463       ],
41464       [
41465         "Bangladesh (বাংলাদেশ)",
41466         "bd",
41467         "880"
41468       ],
41469       [
41470         "Barbados",
41471         "bb",
41472         "1246"
41473       ],
41474       [
41475         "Belarus (Беларусь)",
41476         "by",
41477         "375"
41478       ],
41479       [
41480         "Belgium (België)",
41481         "be",
41482         "32"
41483       ],
41484       [
41485         "Belize",
41486         "bz",
41487         "501"
41488       ],
41489       [
41490         "Benin (Bénin)",
41491         "bj",
41492         "229"
41493       ],
41494       [
41495         "Bermuda",
41496         "bm",
41497         "1441"
41498       ],
41499       [
41500         "Bhutan (འབྲུག)",
41501         "bt",
41502         "975"
41503       ],
41504       [
41505         "Bolivia",
41506         "bo",
41507         "591"
41508       ],
41509       [
41510         "Bosnia and Herzegovina (Босна и Херцеговина)",
41511         "ba",
41512         "387"
41513       ],
41514       [
41515         "Botswana",
41516         "bw",
41517         "267"
41518       ],
41519       [
41520         "Brazil (Brasil)",
41521         "br",
41522         "55"
41523       ],
41524       [
41525         "British Indian Ocean Territory",
41526         "io",
41527         "246"
41528       ],
41529       [
41530         "British Virgin Islands",
41531         "vg",
41532         "1284"
41533       ],
41534       [
41535         "Brunei",
41536         "bn",
41537         "673"
41538       ],
41539       [
41540         "Bulgaria (България)",
41541         "bg",
41542         "359"
41543       ],
41544       [
41545         "Burkina Faso",
41546         "bf",
41547         "226"
41548       ],
41549       [
41550         "Burundi (Uburundi)",
41551         "bi",
41552         "257"
41553       ],
41554       [
41555         "Cambodia (កម្ពុជា)",
41556         "kh",
41557         "855"
41558       ],
41559       [
41560         "Cameroon (Cameroun)",
41561         "cm",
41562         "237"
41563       ],
41564       [
41565         "Canada",
41566         "ca",
41567         "1",
41568         1,
41569         ["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"]
41570       ],
41571       [
41572         "Cape Verde (Kabu Verdi)",
41573         "cv",
41574         "238"
41575       ],
41576       [
41577         "Caribbean Netherlands",
41578         "bq",
41579         "599",
41580         1
41581       ],
41582       [
41583         "Cayman Islands",
41584         "ky",
41585         "1345"
41586       ],
41587       [
41588         "Central African Republic (République centrafricaine)",
41589         "cf",
41590         "236"
41591       ],
41592       [
41593         "Chad (Tchad)",
41594         "td",
41595         "235"
41596       ],
41597       [
41598         "Chile",
41599         "cl",
41600         "56"
41601       ],
41602       [
41603         "China (中国)",
41604         "cn",
41605         "86"
41606       ],
41607       [
41608         "Christmas Island",
41609         "cx",
41610         "61",
41611         2
41612       ],
41613       [
41614         "Cocos (Keeling) Islands",
41615         "cc",
41616         "61",
41617         1
41618       ],
41619       [
41620         "Colombia",
41621         "co",
41622         "57"
41623       ],
41624       [
41625         "Comoros (‫جزر القمر‬‎)",
41626         "km",
41627         "269"
41628       ],
41629       [
41630         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41631         "cd",
41632         "243"
41633       ],
41634       [
41635         "Congo (Republic) (Congo-Brazzaville)",
41636         "cg",
41637         "242"
41638       ],
41639       [
41640         "Cook Islands",
41641         "ck",
41642         "682"
41643       ],
41644       [
41645         "Costa Rica",
41646         "cr",
41647         "506"
41648       ],
41649       [
41650         "Côte d’Ivoire",
41651         "ci",
41652         "225"
41653       ],
41654       [
41655         "Croatia (Hrvatska)",
41656         "hr",
41657         "385"
41658       ],
41659       [
41660         "Cuba",
41661         "cu",
41662         "53"
41663       ],
41664       [
41665         "Curaçao",
41666         "cw",
41667         "599",
41668         0
41669       ],
41670       [
41671         "Cyprus (Κύπρος)",
41672         "cy",
41673         "357"
41674       ],
41675       [
41676         "Czech Republic (Česká republika)",
41677         "cz",
41678         "420"
41679       ],
41680       [
41681         "Denmark (Danmark)",
41682         "dk",
41683         "45"
41684       ],
41685       [
41686         "Djibouti",
41687         "dj",
41688         "253"
41689       ],
41690       [
41691         "Dominica",
41692         "dm",
41693         "1767"
41694       ],
41695       [
41696         "Dominican Republic (República Dominicana)",
41697         "do",
41698         "1",
41699         2,
41700         ["809", "829", "849"]
41701       ],
41702       [
41703         "Ecuador",
41704         "ec",
41705         "593"
41706       ],
41707       [
41708         "Egypt (‫مصر‬‎)",
41709         "eg",
41710         "20"
41711       ],
41712       [
41713         "El Salvador",
41714         "sv",
41715         "503"
41716       ],
41717       [
41718         "Equatorial Guinea (Guinea Ecuatorial)",
41719         "gq",
41720         "240"
41721       ],
41722       [
41723         "Eritrea",
41724         "er",
41725         "291"
41726       ],
41727       [
41728         "Estonia (Eesti)",
41729         "ee",
41730         "372"
41731       ],
41732       [
41733         "Ethiopia",
41734         "et",
41735         "251"
41736       ],
41737       [
41738         "Falkland Islands (Islas Malvinas)",
41739         "fk",
41740         "500"
41741       ],
41742       [
41743         "Faroe Islands (Føroyar)",
41744         "fo",
41745         "298"
41746       ],
41747       [
41748         "Fiji",
41749         "fj",
41750         "679"
41751       ],
41752       [
41753         "Finland (Suomi)",
41754         "fi",
41755         "358",
41756         0
41757       ],
41758       [
41759         "France",
41760         "fr",
41761         "33"
41762       ],
41763       [
41764         "French Guiana (Guyane française)",
41765         "gf",
41766         "594"
41767       ],
41768       [
41769         "French Polynesia (Polynésie française)",
41770         "pf",
41771         "689"
41772       ],
41773       [
41774         "Gabon",
41775         "ga",
41776         "241"
41777       ],
41778       [
41779         "Gambia",
41780         "gm",
41781         "220"
41782       ],
41783       [
41784         "Georgia (საქართველო)",
41785         "ge",
41786         "995"
41787       ],
41788       [
41789         "Germany (Deutschland)",
41790         "de",
41791         "49"
41792       ],
41793       [
41794         "Ghana (Gaana)",
41795         "gh",
41796         "233"
41797       ],
41798       [
41799         "Gibraltar",
41800         "gi",
41801         "350"
41802       ],
41803       [
41804         "Greece (Ελλάδα)",
41805         "gr",
41806         "30"
41807       ],
41808       [
41809         "Greenland (Kalaallit Nunaat)",
41810         "gl",
41811         "299"
41812       ],
41813       [
41814         "Grenada",
41815         "gd",
41816         "1473"
41817       ],
41818       [
41819         "Guadeloupe",
41820         "gp",
41821         "590",
41822         0
41823       ],
41824       [
41825         "Guam",
41826         "gu",
41827         "1671"
41828       ],
41829       [
41830         "Guatemala",
41831         "gt",
41832         "502"
41833       ],
41834       [
41835         "Guernsey",
41836         "gg",
41837         "44",
41838         1
41839       ],
41840       [
41841         "Guinea (Guinée)",
41842         "gn",
41843         "224"
41844       ],
41845       [
41846         "Guinea-Bissau (Guiné Bissau)",
41847         "gw",
41848         "245"
41849       ],
41850       [
41851         "Guyana",
41852         "gy",
41853         "592"
41854       ],
41855       [
41856         "Haiti",
41857         "ht",
41858         "509"
41859       ],
41860       [
41861         "Honduras",
41862         "hn",
41863         "504"
41864       ],
41865       [
41866         "Hong Kong (香港)",
41867         "hk",
41868         "852"
41869       ],
41870       [
41871         "Hungary (Magyarország)",
41872         "hu",
41873         "36"
41874       ],
41875       [
41876         "Iceland (Ísland)",
41877         "is",
41878         "354"
41879       ],
41880       [
41881         "India (भारत)",
41882         "in",
41883         "91"
41884       ],
41885       [
41886         "Indonesia",
41887         "id",
41888         "62"
41889       ],
41890       [
41891         "Iran (‫ایران‬‎)",
41892         "ir",
41893         "98"
41894       ],
41895       [
41896         "Iraq (‫العراق‬‎)",
41897         "iq",
41898         "964"
41899       ],
41900       [
41901         "Ireland",
41902         "ie",
41903         "353"
41904       ],
41905       [
41906         "Isle of Man",
41907         "im",
41908         "44",
41909         2
41910       ],
41911       [
41912         "Israel (‫ישראל‬‎)",
41913         "il",
41914         "972"
41915       ],
41916       [
41917         "Italy (Italia)",
41918         "it",
41919         "39",
41920         0
41921       ],
41922       [
41923         "Jamaica",
41924         "jm",
41925         "1876"
41926       ],
41927       [
41928         "Japan (日本)",
41929         "jp",
41930         "81"
41931       ],
41932       [
41933         "Jersey",
41934         "je",
41935         "44",
41936         3
41937       ],
41938       [
41939         "Jordan (‫الأردن‬‎)",
41940         "jo",
41941         "962"
41942       ],
41943       [
41944         "Kazakhstan (Казахстан)",
41945         "kz",
41946         "7",
41947         1
41948       ],
41949       [
41950         "Kenya",
41951         "ke",
41952         "254"
41953       ],
41954       [
41955         "Kiribati",
41956         "ki",
41957         "686"
41958       ],
41959       [
41960         "Kosovo",
41961         "xk",
41962         "383"
41963       ],
41964       [
41965         "Kuwait (‫الكويت‬‎)",
41966         "kw",
41967         "965"
41968       ],
41969       [
41970         "Kyrgyzstan (Кыргызстан)",
41971         "kg",
41972         "996"
41973       ],
41974       [
41975         "Laos (ລາວ)",
41976         "la",
41977         "856"
41978       ],
41979       [
41980         "Latvia (Latvija)",
41981         "lv",
41982         "371"
41983       ],
41984       [
41985         "Lebanon (‫لبنان‬‎)",
41986         "lb",
41987         "961"
41988       ],
41989       [
41990         "Lesotho",
41991         "ls",
41992         "266"
41993       ],
41994       [
41995         "Liberia",
41996         "lr",
41997         "231"
41998       ],
41999       [
42000         "Libya (‫ليبيا‬‎)",
42001         "ly",
42002         "218"
42003       ],
42004       [
42005         "Liechtenstein",
42006         "li",
42007         "423"
42008       ],
42009       [
42010         "Lithuania (Lietuva)",
42011         "lt",
42012         "370"
42013       ],
42014       [
42015         "Luxembourg",
42016         "lu",
42017         "352"
42018       ],
42019       [
42020         "Macau (澳門)",
42021         "mo",
42022         "853"
42023       ],
42024       [
42025         "Macedonia (FYROM) (Македонија)",
42026         "mk",
42027         "389"
42028       ],
42029       [
42030         "Madagascar (Madagasikara)",
42031         "mg",
42032         "261"
42033       ],
42034       [
42035         "Malawi",
42036         "mw",
42037         "265"
42038       ],
42039       [
42040         "Malaysia",
42041         "my",
42042         "60"
42043       ],
42044       [
42045         "Maldives",
42046         "mv",
42047         "960"
42048       ],
42049       [
42050         "Mali",
42051         "ml",
42052         "223"
42053       ],
42054       [
42055         "Malta",
42056         "mt",
42057         "356"
42058       ],
42059       [
42060         "Marshall Islands",
42061         "mh",
42062         "692"
42063       ],
42064       [
42065         "Martinique",
42066         "mq",
42067         "596"
42068       ],
42069       [
42070         "Mauritania (‫موريتانيا‬‎)",
42071         "mr",
42072         "222"
42073       ],
42074       [
42075         "Mauritius (Moris)",
42076         "mu",
42077         "230"
42078       ],
42079       [
42080         "Mayotte",
42081         "yt",
42082         "262",
42083         1
42084       ],
42085       [
42086         "Mexico (México)",
42087         "mx",
42088         "52"
42089       ],
42090       [
42091         "Micronesia",
42092         "fm",
42093         "691"
42094       ],
42095       [
42096         "Moldova (Republica Moldova)",
42097         "md",
42098         "373"
42099       ],
42100       [
42101         "Monaco",
42102         "mc",
42103         "377"
42104       ],
42105       [
42106         "Mongolia (Монгол)",
42107         "mn",
42108         "976"
42109       ],
42110       [
42111         "Montenegro (Crna Gora)",
42112         "me",
42113         "382"
42114       ],
42115       [
42116         "Montserrat",
42117         "ms",
42118         "1664"
42119       ],
42120       [
42121         "Morocco (‫المغرب‬‎)",
42122         "ma",
42123         "212",
42124         0
42125       ],
42126       [
42127         "Mozambique (Moçambique)",
42128         "mz",
42129         "258"
42130       ],
42131       [
42132         "Myanmar (Burma) (မြန်မာ)",
42133         "mm",
42134         "95"
42135       ],
42136       [
42137         "Namibia (Namibië)",
42138         "na",
42139         "264"
42140       ],
42141       [
42142         "Nauru",
42143         "nr",
42144         "674"
42145       ],
42146       [
42147         "Nepal (नेपाल)",
42148         "np",
42149         "977"
42150       ],
42151       [
42152         "Netherlands (Nederland)",
42153         "nl",
42154         "31"
42155       ],
42156       [
42157         "New Caledonia (Nouvelle-Calédonie)",
42158         "nc",
42159         "687"
42160       ],
42161       [
42162         "New Zealand",
42163         "nz",
42164         "64"
42165       ],
42166       [
42167         "Nicaragua",
42168         "ni",
42169         "505"
42170       ],
42171       [
42172         "Niger (Nijar)",
42173         "ne",
42174         "227"
42175       ],
42176       [
42177         "Nigeria",
42178         "ng",
42179         "234"
42180       ],
42181       [
42182         "Niue",
42183         "nu",
42184         "683"
42185       ],
42186       [
42187         "Norfolk Island",
42188         "nf",
42189         "672"
42190       ],
42191       [
42192         "North Korea (조선 민주주의 인민 공화국)",
42193         "kp",
42194         "850"
42195       ],
42196       [
42197         "Northern Mariana Islands",
42198         "mp",
42199         "1670"
42200       ],
42201       [
42202         "Norway (Norge)",
42203         "no",
42204         "47",
42205         0
42206       ],
42207       [
42208         "Oman (‫عُمان‬‎)",
42209         "om",
42210         "968"
42211       ],
42212       [
42213         "Pakistan (‫پاکستان‬‎)",
42214         "pk",
42215         "92"
42216       ],
42217       [
42218         "Palau",
42219         "pw",
42220         "680"
42221       ],
42222       [
42223         "Palestine (‫فلسطين‬‎)",
42224         "ps",
42225         "970"
42226       ],
42227       [
42228         "Panama (Panamá)",
42229         "pa",
42230         "507"
42231       ],
42232       [
42233         "Papua New Guinea",
42234         "pg",
42235         "675"
42236       ],
42237       [
42238         "Paraguay",
42239         "py",
42240         "595"
42241       ],
42242       [
42243         "Peru (Perú)",
42244         "pe",
42245         "51"
42246       ],
42247       [
42248         "Philippines",
42249         "ph",
42250         "63"
42251       ],
42252       [
42253         "Poland (Polska)",
42254         "pl",
42255         "48"
42256       ],
42257       [
42258         "Portugal",
42259         "pt",
42260         "351"
42261       ],
42262       [
42263         "Puerto Rico",
42264         "pr",
42265         "1",
42266         3,
42267         ["787", "939"]
42268       ],
42269       [
42270         "Qatar (‫قطر‬‎)",
42271         "qa",
42272         "974"
42273       ],
42274       [
42275         "Réunion (La Réunion)",
42276         "re",
42277         "262",
42278         0
42279       ],
42280       [
42281         "Romania (România)",
42282         "ro",
42283         "40"
42284       ],
42285       [
42286         "Russia (Россия)",
42287         "ru",
42288         "7",
42289         0
42290       ],
42291       [
42292         "Rwanda",
42293         "rw",
42294         "250"
42295       ],
42296       [
42297         "Saint Barthélemy",
42298         "bl",
42299         "590",
42300         1
42301       ],
42302       [
42303         "Saint Helena",
42304         "sh",
42305         "290"
42306       ],
42307       [
42308         "Saint Kitts and Nevis",
42309         "kn",
42310         "1869"
42311       ],
42312       [
42313         "Saint Lucia",
42314         "lc",
42315         "1758"
42316       ],
42317       [
42318         "Saint Martin (Saint-Martin (partie française))",
42319         "mf",
42320         "590",
42321         2
42322       ],
42323       [
42324         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42325         "pm",
42326         "508"
42327       ],
42328       [
42329         "Saint Vincent and the Grenadines",
42330         "vc",
42331         "1784"
42332       ],
42333       [
42334         "Samoa",
42335         "ws",
42336         "685"
42337       ],
42338       [
42339         "San Marino",
42340         "sm",
42341         "378"
42342       ],
42343       [
42344         "São Tomé and Príncipe (São Tomé e Príncipe)",
42345         "st",
42346         "239"
42347       ],
42348       [
42349         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42350         "sa",
42351         "966"
42352       ],
42353       [
42354         "Senegal (Sénégal)",
42355         "sn",
42356         "221"
42357       ],
42358       [
42359         "Serbia (Србија)",
42360         "rs",
42361         "381"
42362       ],
42363       [
42364         "Seychelles",
42365         "sc",
42366         "248"
42367       ],
42368       [
42369         "Sierra Leone",
42370         "sl",
42371         "232"
42372       ],
42373       [
42374         "Singapore",
42375         "sg",
42376         "65"
42377       ],
42378       [
42379         "Sint Maarten",
42380         "sx",
42381         "1721"
42382       ],
42383       [
42384         "Slovakia (Slovensko)",
42385         "sk",
42386         "421"
42387       ],
42388       [
42389         "Slovenia (Slovenija)",
42390         "si",
42391         "386"
42392       ],
42393       [
42394         "Solomon Islands",
42395         "sb",
42396         "677"
42397       ],
42398       [
42399         "Somalia (Soomaaliya)",
42400         "so",
42401         "252"
42402       ],
42403       [
42404         "South Africa",
42405         "za",
42406         "27"
42407       ],
42408       [
42409         "South Korea (대한민국)",
42410         "kr",
42411         "82"
42412       ],
42413       [
42414         "South Sudan (‫جنوب السودان‬‎)",
42415         "ss",
42416         "211"
42417       ],
42418       [
42419         "Spain (España)",
42420         "es",
42421         "34"
42422       ],
42423       [
42424         "Sri Lanka (ශ්‍රී ලංකාව)",
42425         "lk",
42426         "94"
42427       ],
42428       [
42429         "Sudan (‫السودان‬‎)",
42430         "sd",
42431         "249"
42432       ],
42433       [
42434         "Suriname",
42435         "sr",
42436         "597"
42437       ],
42438       [
42439         "Svalbard and Jan Mayen",
42440         "sj",
42441         "47",
42442         1
42443       ],
42444       [
42445         "Swaziland",
42446         "sz",
42447         "268"
42448       ],
42449       [
42450         "Sweden (Sverige)",
42451         "se",
42452         "46"
42453       ],
42454       [
42455         "Switzerland (Schweiz)",
42456         "ch",
42457         "41"
42458       ],
42459       [
42460         "Syria (‫سوريا‬‎)",
42461         "sy",
42462         "963"
42463       ],
42464       [
42465         "Taiwan (台灣)",
42466         "tw",
42467         "886"
42468       ],
42469       [
42470         "Tajikistan",
42471         "tj",
42472         "992"
42473       ],
42474       [
42475         "Tanzania",
42476         "tz",
42477         "255"
42478       ],
42479       [
42480         "Thailand (ไทย)",
42481         "th",
42482         "66"
42483       ],
42484       [
42485         "Timor-Leste",
42486         "tl",
42487         "670"
42488       ],
42489       [
42490         "Togo",
42491         "tg",
42492         "228"
42493       ],
42494       [
42495         "Tokelau",
42496         "tk",
42497         "690"
42498       ],
42499       [
42500         "Tonga",
42501         "to",
42502         "676"
42503       ],
42504       [
42505         "Trinidad and Tobago",
42506         "tt",
42507         "1868"
42508       ],
42509       [
42510         "Tunisia (‫تونس‬‎)",
42511         "tn",
42512         "216"
42513       ],
42514       [
42515         "Turkey (Türkiye)",
42516         "tr",
42517         "90"
42518       ],
42519       [
42520         "Turkmenistan",
42521         "tm",
42522         "993"
42523       ],
42524       [
42525         "Turks and Caicos Islands",
42526         "tc",
42527         "1649"
42528       ],
42529       [
42530         "Tuvalu",
42531         "tv",
42532         "688"
42533       ],
42534       [
42535         "U.S. Virgin Islands",
42536         "vi",
42537         "1340"
42538       ],
42539       [
42540         "Uganda",
42541         "ug",
42542         "256"
42543       ],
42544       [
42545         "Ukraine (Україна)",
42546         "ua",
42547         "380"
42548       ],
42549       [
42550         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42551         "ae",
42552         "971"
42553       ],
42554       [
42555         "United Kingdom",
42556         "gb",
42557         "44",
42558         0
42559       ],
42560       [
42561         "United States",
42562         "us",
42563         "1",
42564         0
42565       ],
42566       [
42567         "Uruguay",
42568         "uy",
42569         "598"
42570       ],
42571       [
42572         "Uzbekistan (Oʻzbekiston)",
42573         "uz",
42574         "998"
42575       ],
42576       [
42577         "Vanuatu",
42578         "vu",
42579         "678"
42580       ],
42581       [
42582         "Vatican City (Città del Vaticano)",
42583         "va",
42584         "39",
42585         1
42586       ],
42587       [
42588         "Venezuela",
42589         "ve",
42590         "58"
42591       ],
42592       [
42593         "Vietnam (Việt Nam)",
42594         "vn",
42595         "84"
42596       ],
42597       [
42598         "Wallis and Futuna (Wallis-et-Futuna)",
42599         "wf",
42600         "681"
42601       ],
42602       [
42603         "Western Sahara (‫الصحراء الغربية‬‎)",
42604         "eh",
42605         "212",
42606         1
42607       ],
42608       [
42609         "Yemen (‫اليمن‬‎)",
42610         "ye",
42611         "967"
42612       ],
42613       [
42614         "Zambia",
42615         "zm",
42616         "260"
42617       ],
42618       [
42619         "Zimbabwe",
42620         "zw",
42621         "263"
42622       ],
42623       [
42624         "Åland Islands",
42625         "ax",
42626         "358",
42627         1
42628       ]
42629   ];
42630   
42631   return d;
42632 }/**
42633 *    This script refer to:
42634 *    Title: International Telephone Input
42635 *    Author: Jack O'Connor
42636 *    Code version:  v12.1.12
42637 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42638 **/
42639
42640 /**
42641  * @class Roo.bootstrap.PhoneInput
42642  * @extends Roo.bootstrap.TriggerField
42643  * An input with International dial-code selection
42644  
42645  * @cfg {String} defaultDialCode default '+852'
42646  * @cfg {Array} preferedCountries default []
42647   
42648  * @constructor
42649  * Create a new PhoneInput.
42650  * @param {Object} config Configuration options
42651  */
42652
42653 Roo.bootstrap.PhoneInput = function(config) {
42654     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42655 };
42656
42657 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42658         
42659         listWidth: undefined,
42660         
42661         selectedClass: 'active',
42662         
42663         invalidClass : "has-warning",
42664         
42665         validClass: 'has-success',
42666         
42667         allowed: '0123456789',
42668         
42669         max_length: 15,
42670         
42671         /**
42672          * @cfg {String} defaultDialCode The default dial code when initializing the input
42673          */
42674         defaultDialCode: '+852',
42675         
42676         /**
42677          * @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
42678          */
42679         preferedCountries: false,
42680         
42681         getAutoCreate : function()
42682         {
42683             var data = Roo.bootstrap.PhoneInputData();
42684             var align = this.labelAlign || this.parentLabelAlign();
42685             var id = Roo.id();
42686             
42687             this.allCountries = [];
42688             this.dialCodeMapping = [];
42689             
42690             for (var i = 0; i < data.length; i++) {
42691               var c = data[i];
42692               this.allCountries[i] = {
42693                 name: c[0],
42694                 iso2: c[1],
42695                 dialCode: c[2],
42696                 priority: c[3] || 0,
42697                 areaCodes: c[4] || null
42698               };
42699               this.dialCodeMapping[c[2]] = {
42700                   name: c[0],
42701                   iso2: c[1],
42702                   priority: c[3] || 0,
42703                   areaCodes: c[4] || null
42704               };
42705             }
42706             
42707             var cfg = {
42708                 cls: 'form-group',
42709                 cn: []
42710             };
42711             
42712             var input =  {
42713                 tag: 'input',
42714                 id : id,
42715                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42716                 maxlength: this.max_length,
42717                 cls : 'form-control tel-input',
42718                 autocomplete: 'new-password'
42719             };
42720             
42721             var hiddenInput = {
42722                 tag: 'input',
42723                 type: 'hidden',
42724                 cls: 'hidden-tel-input'
42725             };
42726             
42727             if (this.name) {
42728                 hiddenInput.name = this.name;
42729             }
42730             
42731             if (this.disabled) {
42732                 input.disabled = true;
42733             }
42734             
42735             var flag_container = {
42736                 tag: 'div',
42737                 cls: 'flag-box',
42738                 cn: [
42739                     {
42740                         tag: 'div',
42741                         cls: 'flag'
42742                     },
42743                     {
42744                         tag: 'div',
42745                         cls: 'caret'
42746                     }
42747                 ]
42748             };
42749             
42750             var box = {
42751                 tag: 'div',
42752                 cls: this.hasFeedback ? 'has-feedback' : '',
42753                 cn: [
42754                     hiddenInput,
42755                     input,
42756                     {
42757                         tag: 'input',
42758                         cls: 'dial-code-holder',
42759                         disabled: true
42760                     }
42761                 ]
42762             };
42763             
42764             var container = {
42765                 cls: 'roo-select2-container input-group',
42766                 cn: [
42767                     flag_container,
42768                     box
42769                 ]
42770             };
42771             
42772             if (this.fieldLabel.length) {
42773                 var indicator = {
42774                     tag: 'i',
42775                     tooltip: 'This field is required'
42776                 };
42777                 
42778                 var label = {
42779                     tag: 'label',
42780                     'for':  id,
42781                     cls: 'control-label',
42782                     cn: []
42783                 };
42784                 
42785                 var label_text = {
42786                     tag: 'span',
42787                     html: this.fieldLabel
42788                 };
42789                 
42790                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42791                 label.cn = [
42792                     indicator,
42793                     label_text
42794                 ];
42795                 
42796                 if(this.indicatorpos == 'right') {
42797                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42798                     label.cn = [
42799                         label_text,
42800                         indicator
42801                     ];
42802                 }
42803                 
42804                 if(align == 'left') {
42805                     container = {
42806                         tag: 'div',
42807                         cn: [
42808                             container
42809                         ]
42810                     };
42811                     
42812                     if(this.labelWidth > 12){
42813                         label.style = "width: " + this.labelWidth + 'px';
42814                     }
42815                     if(this.labelWidth < 13 && this.labelmd == 0){
42816                         this.labelmd = this.labelWidth;
42817                     }
42818                     if(this.labellg > 0){
42819                         label.cls += ' col-lg-' + this.labellg;
42820                         input.cls += ' col-lg-' + (12 - this.labellg);
42821                     }
42822                     if(this.labelmd > 0){
42823                         label.cls += ' col-md-' + this.labelmd;
42824                         container.cls += ' col-md-' + (12 - this.labelmd);
42825                     }
42826                     if(this.labelsm > 0){
42827                         label.cls += ' col-sm-' + this.labelsm;
42828                         container.cls += ' col-sm-' + (12 - this.labelsm);
42829                     }
42830                     if(this.labelxs > 0){
42831                         label.cls += ' col-xs-' + this.labelxs;
42832                         container.cls += ' col-xs-' + (12 - this.labelxs);
42833                     }
42834                 }
42835             }
42836             
42837             cfg.cn = [
42838                 label,
42839                 container
42840             ];
42841             
42842             var settings = this;
42843             
42844             ['xs','sm','md','lg'].map(function(size){
42845                 if (settings[size]) {
42846                     cfg.cls += ' col-' + size + '-' + settings[size];
42847                 }
42848             });
42849             
42850             this.store = new Roo.data.Store({
42851                 proxy : new Roo.data.MemoryProxy({}),
42852                 reader : new Roo.data.JsonReader({
42853                     fields : [
42854                         {
42855                             'name' : 'name',
42856                             'type' : 'string'
42857                         },
42858                         {
42859                             'name' : 'iso2',
42860                             'type' : 'string'
42861                         },
42862                         {
42863                             'name' : 'dialCode',
42864                             'type' : 'string'
42865                         },
42866                         {
42867                             'name' : 'priority',
42868                             'type' : 'string'
42869                         },
42870                         {
42871                             'name' : 'areaCodes',
42872                             'type' : 'string'
42873                         }
42874                     ]
42875                 })
42876             });
42877             
42878             if(!this.preferedCountries) {
42879                 this.preferedCountries = [
42880                     'hk',
42881                     'gb',
42882                     'us'
42883                 ];
42884             }
42885             
42886             var p = this.preferedCountries.reverse();
42887             
42888             if(p) {
42889                 for (var i = 0; i < p.length; i++) {
42890                     for (var j = 0; j < this.allCountries.length; j++) {
42891                         if(this.allCountries[j].iso2 == p[i]) {
42892                             var t = this.allCountries[j];
42893                             this.allCountries.splice(j,1);
42894                             this.allCountries.unshift(t);
42895                         }
42896                     } 
42897                 }
42898             }
42899             
42900             this.store.proxy.data = {
42901                 success: true,
42902                 data: this.allCountries
42903             };
42904             
42905             return cfg;
42906         },
42907         
42908         initEvents : function()
42909         {
42910             this.createList();
42911             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42912             
42913             this.indicator = this.indicatorEl();
42914             this.flag = this.flagEl();
42915             this.dialCodeHolder = this.dialCodeHolderEl();
42916             
42917             this.trigger = this.el.select('div.flag-box',true).first();
42918             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42919             
42920             var _this = this;
42921             
42922             (function(){
42923                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42924                 _this.list.setWidth(lw);
42925             }).defer(100);
42926             
42927             this.list.on('mouseover', this.onViewOver, this);
42928             this.list.on('mousemove', this.onViewMove, this);
42929             this.inputEl().on("keyup", this.onKeyUp, this);
42930             this.inputEl().on("keypress", this.onKeyPress, this);
42931             
42932             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42933
42934             this.view = new Roo.View(this.list, this.tpl, {
42935                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42936             });
42937             
42938             this.view.on('click', this.onViewClick, this);
42939             this.setValue(this.defaultDialCode);
42940         },
42941         
42942         onTriggerClick : function(e)
42943         {
42944             Roo.log('trigger click');
42945             if(this.disabled){
42946                 return;
42947             }
42948             
42949             if(this.isExpanded()){
42950                 this.collapse();
42951                 this.hasFocus = false;
42952             }else {
42953                 this.store.load({});
42954                 this.hasFocus = true;
42955                 this.expand();
42956             }
42957         },
42958         
42959         isExpanded : function()
42960         {
42961             return this.list.isVisible();
42962         },
42963         
42964         collapse : function()
42965         {
42966             if(!this.isExpanded()){
42967                 return;
42968             }
42969             this.list.hide();
42970             Roo.get(document).un('mousedown', this.collapseIf, this);
42971             Roo.get(document).un('mousewheel', this.collapseIf, this);
42972             this.fireEvent('collapse', this);
42973             this.validate();
42974         },
42975         
42976         expand : function()
42977         {
42978             Roo.log('expand');
42979
42980             if(this.isExpanded() || !this.hasFocus){
42981                 return;
42982             }
42983             
42984             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42985             this.list.setWidth(lw);
42986             
42987             this.list.show();
42988             this.restrictHeight();
42989             
42990             Roo.get(document).on('mousedown', this.collapseIf, this);
42991             Roo.get(document).on('mousewheel', this.collapseIf, this);
42992             
42993             this.fireEvent('expand', this);
42994         },
42995         
42996         restrictHeight : function()
42997         {
42998             this.list.alignTo(this.inputEl(), this.listAlign);
42999             this.list.alignTo(this.inputEl(), this.listAlign);
43000         },
43001         
43002         onViewOver : function(e, t)
43003         {
43004             if(this.inKeyMode){
43005                 return;
43006             }
43007             var item = this.view.findItemFromChild(t);
43008             
43009             if(item){
43010                 var index = this.view.indexOf(item);
43011                 this.select(index, false);
43012             }
43013         },
43014
43015         // private
43016         onViewClick : function(view, doFocus, el, e)
43017         {
43018             var index = this.view.getSelectedIndexes()[0];
43019             
43020             var r = this.store.getAt(index);
43021             
43022             if(r){
43023                 this.onSelect(r, index);
43024             }
43025             if(doFocus !== false && !this.blockFocus){
43026                 this.inputEl().focus();
43027             }
43028         },
43029         
43030         onViewMove : function(e, t)
43031         {
43032             this.inKeyMode = false;
43033         },
43034         
43035         select : function(index, scrollIntoView)
43036         {
43037             this.selectedIndex = index;
43038             this.view.select(index);
43039             if(scrollIntoView !== false){
43040                 var el = this.view.getNode(index);
43041                 if(el){
43042                     this.list.scrollChildIntoView(el, false);
43043                 }
43044             }
43045         },
43046         
43047         createList : function()
43048         {
43049             this.list = Roo.get(document.body).createChild({
43050                 tag: 'ul',
43051                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43052                 style: 'display:none'
43053             });
43054             
43055             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43056         },
43057         
43058         collapseIf : function(e)
43059         {
43060             var in_combo  = e.within(this.el);
43061             var in_list =  e.within(this.list);
43062             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43063             
43064             if (in_combo || in_list || is_list) {
43065                 return;
43066             }
43067             this.collapse();
43068         },
43069         
43070         onSelect : function(record, index)
43071         {
43072             if(this.fireEvent('beforeselect', this, record, index) !== false){
43073                 
43074                 this.setFlagClass(record.data.iso2);
43075                 this.setDialCode(record.data.dialCode);
43076                 this.hasFocus = false;
43077                 this.collapse();
43078                 this.fireEvent('select', this, record, index);
43079             }
43080         },
43081         
43082         flagEl : function()
43083         {
43084             var flag = this.el.select('div.flag',true).first();
43085             if(!flag){
43086                 return false;
43087             }
43088             return flag;
43089         },
43090         
43091         dialCodeHolderEl : function()
43092         {
43093             var d = this.el.select('input.dial-code-holder',true).first();
43094             if(!d){
43095                 return false;
43096             }
43097             return d;
43098         },
43099         
43100         setDialCode : function(v)
43101         {
43102             this.dialCodeHolder.dom.value = '+'+v;
43103         },
43104         
43105         setFlagClass : function(n)
43106         {
43107             this.flag.dom.className = 'flag '+n;
43108         },
43109         
43110         getValue : function()
43111         {
43112             var v = this.inputEl().getValue();
43113             if(this.dialCodeHolder) {
43114                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43115             }
43116             return v;
43117         },
43118         
43119         setValue : function(v)
43120         {
43121             var d = this.getDialCode(v);
43122             
43123             //invalid dial code
43124             if(v.length == 0 || !d || d.length == 0) {
43125                 if(this.rendered){
43126                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43127                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43128                 }
43129                 return;
43130             }
43131             
43132             //valid dial code
43133             this.setFlagClass(this.dialCodeMapping[d].iso2);
43134             this.setDialCode(d);
43135             this.inputEl().dom.value = v.replace('+'+d,'');
43136             this.hiddenEl().dom.value = this.getValue();
43137             
43138             this.validate();
43139         },
43140         
43141         getDialCode : function(v)
43142         {
43143             v = v ||  '';
43144             
43145             if (v.length == 0) {
43146                 return this.dialCodeHolder.dom.value;
43147             }
43148             
43149             var dialCode = "";
43150             if (v.charAt(0) != "+") {
43151                 return false;
43152             }
43153             var numericChars = "";
43154             for (var i = 1; i < v.length; i++) {
43155               var c = v.charAt(i);
43156               if (!isNaN(c)) {
43157                 numericChars += c;
43158                 if (this.dialCodeMapping[numericChars]) {
43159                   dialCode = v.substr(1, i);
43160                 }
43161                 if (numericChars.length == 4) {
43162                   break;
43163                 }
43164               }
43165             }
43166             return dialCode;
43167         },
43168         
43169         reset : function()
43170         {
43171             this.setValue(this.defaultDialCode);
43172             this.validate();
43173         },
43174         
43175         hiddenEl : function()
43176         {
43177             return this.el.select('input.hidden-tel-input',true).first();
43178         },
43179         
43180         // after setting val
43181         onKeyUp : function(e){
43182             this.setValue(this.getValue());
43183         },
43184         
43185         onKeyPress : function(e){
43186             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43187                 e.stopEvent();
43188             }
43189         }
43190         
43191 });
43192 /**
43193  * @class Roo.bootstrap.MoneyField
43194  * @extends Roo.bootstrap.ComboBox
43195  * Bootstrap MoneyField class
43196  * 
43197  * @constructor
43198  * Create a new MoneyField.
43199  * @param {Object} config Configuration options
43200  */
43201
43202 Roo.bootstrap.MoneyField = function(config) {
43203     
43204     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43205     
43206 };
43207
43208 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43209     
43210     /**
43211      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43212      */
43213     allowDecimals : true,
43214     /**
43215      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43216      */
43217     decimalSeparator : ".",
43218     /**
43219      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43220      */
43221     decimalPrecision : 0,
43222     /**
43223      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43224      */
43225     allowNegative : true,
43226     /**
43227      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43228      */
43229     allowZero: true,
43230     /**
43231      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43232      */
43233     minValue : Number.NEGATIVE_INFINITY,
43234     /**
43235      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43236      */
43237     maxValue : Number.MAX_VALUE,
43238     /**
43239      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43240      */
43241     minText : "The minimum value for this field is {0}",
43242     /**
43243      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43244      */
43245     maxText : "The maximum value for this field is {0}",
43246     /**
43247      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43248      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43249      */
43250     nanText : "{0} is not a valid number",
43251     /**
43252      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43253      */
43254     castInt : true,
43255     /**
43256      * @cfg {String} defaults currency of the MoneyField
43257      * value should be in lkey
43258      */
43259     defaultCurrency : false,
43260     /**
43261      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43262      */
43263     thousandsDelimiter : false,
43264     /**
43265      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43266      */
43267     max_length: false,
43268     
43269     inputlg : 9,
43270     inputmd : 9,
43271     inputsm : 9,
43272     inputxs : 6,
43273     
43274     store : false,
43275     
43276     getAutoCreate : function()
43277     {
43278         var align = this.labelAlign || this.parentLabelAlign();
43279         
43280         var id = Roo.id();
43281
43282         var cfg = {
43283             cls: 'form-group',
43284             cn: []
43285         };
43286
43287         var input =  {
43288             tag: 'input',
43289             id : id,
43290             cls : 'form-control roo-money-amount-input',
43291             autocomplete: 'new-password'
43292         };
43293         
43294         var hiddenInput = {
43295             tag: 'input',
43296             type: 'hidden',
43297             id: Roo.id(),
43298             cls: 'hidden-number-input'
43299         };
43300         
43301         if(this.max_length) {
43302             input.maxlength = this.max_length; 
43303         }
43304         
43305         if (this.name) {
43306             hiddenInput.name = this.name;
43307         }
43308
43309         if (this.disabled) {
43310             input.disabled = true;
43311         }
43312
43313         var clg = 12 - this.inputlg;
43314         var cmd = 12 - this.inputmd;
43315         var csm = 12 - this.inputsm;
43316         var cxs = 12 - this.inputxs;
43317         
43318         var container = {
43319             tag : 'div',
43320             cls : 'row roo-money-field',
43321             cn : [
43322                 {
43323                     tag : 'div',
43324                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43325                     cn : [
43326                         {
43327                             tag : 'div',
43328                             cls: 'roo-select2-container input-group',
43329                             cn: [
43330                                 {
43331                                     tag : 'input',
43332                                     cls : 'form-control roo-money-currency-input',
43333                                     autocomplete: 'new-password',
43334                                     readOnly : 1,
43335                                     name : this.currencyName
43336                                 },
43337                                 {
43338                                     tag :'span',
43339                                     cls : 'input-group-addon',
43340                                     cn : [
43341                                         {
43342                                             tag: 'span',
43343                                             cls: 'caret'
43344                                         }
43345                                     ]
43346                                 }
43347                             ]
43348                         }
43349                     ]
43350                 },
43351                 {
43352                     tag : 'div',
43353                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43354                     cn : [
43355                         {
43356                             tag: 'div',
43357                             cls: this.hasFeedback ? 'has-feedback' : '',
43358                             cn: [
43359                                 input
43360                             ]
43361                         }
43362                     ]
43363                 }
43364             ]
43365             
43366         };
43367         
43368         if (this.fieldLabel.length) {
43369             var indicator = {
43370                 tag: 'i',
43371                 tooltip: 'This field is required'
43372             };
43373
43374             var label = {
43375                 tag: 'label',
43376                 'for':  id,
43377                 cls: 'control-label',
43378                 cn: []
43379             };
43380
43381             var label_text = {
43382                 tag: 'span',
43383                 html: this.fieldLabel
43384             };
43385
43386             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43387             label.cn = [
43388                 indicator,
43389                 label_text
43390             ];
43391
43392             if(this.indicatorpos == 'right') {
43393                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43394                 label.cn = [
43395                     label_text,
43396                     indicator
43397                 ];
43398             }
43399
43400             if(align == 'left') {
43401                 container = {
43402                     tag: 'div',
43403                     cn: [
43404                         container
43405                     ]
43406                 };
43407
43408                 if(this.labelWidth > 12){
43409                     label.style = "width: " + this.labelWidth + 'px';
43410                 }
43411                 if(this.labelWidth < 13 && this.labelmd == 0){
43412                     this.labelmd = this.labelWidth;
43413                 }
43414                 if(this.labellg > 0){
43415                     label.cls += ' col-lg-' + this.labellg;
43416                     input.cls += ' col-lg-' + (12 - this.labellg);
43417                 }
43418                 if(this.labelmd > 0){
43419                     label.cls += ' col-md-' + this.labelmd;
43420                     container.cls += ' col-md-' + (12 - this.labelmd);
43421                 }
43422                 if(this.labelsm > 0){
43423                     label.cls += ' col-sm-' + this.labelsm;
43424                     container.cls += ' col-sm-' + (12 - this.labelsm);
43425                 }
43426                 if(this.labelxs > 0){
43427                     label.cls += ' col-xs-' + this.labelxs;
43428                     container.cls += ' col-xs-' + (12 - this.labelxs);
43429                 }
43430             }
43431         }
43432
43433         cfg.cn = [
43434             label,
43435             container,
43436             hiddenInput
43437         ];
43438         
43439         var settings = this;
43440
43441         ['xs','sm','md','lg'].map(function(size){
43442             if (settings[size]) {
43443                 cfg.cls += ' col-' + size + '-' + settings[size];
43444             }
43445         });
43446         
43447         return cfg;
43448     },
43449     
43450     initEvents : function()
43451     {
43452         this.indicator = this.indicatorEl();
43453         
43454         this.initCurrencyEvent();
43455         
43456         this.initNumberEvent();
43457     },
43458     
43459     initCurrencyEvent : function()
43460     {
43461         if (!this.store) {
43462             throw "can not find store for combo";
43463         }
43464         
43465         this.store = Roo.factory(this.store, Roo.data);
43466         this.store.parent = this;
43467         
43468         this.createList();
43469         
43470         this.triggerEl = this.el.select('.input-group-addon', true).first();
43471         
43472         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43473         
43474         var _this = this;
43475         
43476         (function(){
43477             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43478             _this.list.setWidth(lw);
43479         }).defer(100);
43480         
43481         this.list.on('mouseover', this.onViewOver, this);
43482         this.list.on('mousemove', this.onViewMove, this);
43483         this.list.on('scroll', this.onViewScroll, this);
43484         
43485         if(!this.tpl){
43486             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43487         }
43488         
43489         this.view = new Roo.View(this.list, this.tpl, {
43490             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43491         });
43492         
43493         this.view.on('click', this.onViewClick, this);
43494         
43495         this.store.on('beforeload', this.onBeforeLoad, this);
43496         this.store.on('load', this.onLoad, this);
43497         this.store.on('loadexception', this.onLoadException, this);
43498         
43499         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43500             "up" : function(e){
43501                 this.inKeyMode = true;
43502                 this.selectPrev();
43503             },
43504
43505             "down" : function(e){
43506                 if(!this.isExpanded()){
43507                     this.onTriggerClick();
43508                 }else{
43509                     this.inKeyMode = true;
43510                     this.selectNext();
43511                 }
43512             },
43513
43514             "enter" : function(e){
43515                 this.collapse();
43516                 
43517                 if(this.fireEvent("specialkey", this, e)){
43518                     this.onViewClick(false);
43519                 }
43520                 
43521                 return true;
43522             },
43523
43524             "esc" : function(e){
43525                 this.collapse();
43526             },
43527
43528             "tab" : function(e){
43529                 this.collapse();
43530                 
43531                 if(this.fireEvent("specialkey", this, e)){
43532                     this.onViewClick(false);
43533                 }
43534                 
43535                 return true;
43536             },
43537
43538             scope : this,
43539
43540             doRelay : function(foo, bar, hname){
43541                 if(hname == 'down' || this.scope.isExpanded()){
43542                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43543                 }
43544                 return true;
43545             },
43546
43547             forceKeyDown: true
43548         });
43549         
43550         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43551         
43552     },
43553     
43554     initNumberEvent : function(e)
43555     {
43556         this.inputEl().on("keydown" , this.fireKey,  this);
43557         this.inputEl().on("focus", this.onFocus,  this);
43558         this.inputEl().on("blur", this.onBlur,  this);
43559         
43560         this.inputEl().relayEvent('keyup', this);
43561         
43562         if(this.indicator){
43563             this.indicator.addClass('invisible');
43564         }
43565  
43566         this.originalValue = this.getValue();
43567         
43568         if(this.validationEvent == 'keyup'){
43569             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43570             this.inputEl().on('keyup', this.filterValidation, this);
43571         }
43572         else if(this.validationEvent !== false){
43573             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43574         }
43575         
43576         if(this.selectOnFocus){
43577             this.on("focus", this.preFocus, this);
43578             
43579         }
43580         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43581             this.inputEl().on("keypress", this.filterKeys, this);
43582         } else {
43583             this.inputEl().relayEvent('keypress', this);
43584         }
43585         
43586         var allowed = "0123456789";
43587         
43588         if(this.allowDecimals){
43589             allowed += this.decimalSeparator;
43590         }
43591         
43592         if(this.allowNegative){
43593             allowed += "-";
43594         }
43595         
43596         if(this.thousandsDelimiter) {
43597             allowed += ",";
43598         }
43599         
43600         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43601         
43602         var keyPress = function(e){
43603             
43604             var k = e.getKey();
43605             
43606             var c = e.getCharCode();
43607             
43608             if(
43609                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43610                     allowed.indexOf(String.fromCharCode(c)) === -1
43611             ){
43612                 e.stopEvent();
43613                 return;
43614             }
43615             
43616             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43617                 return;
43618             }
43619             
43620             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43621                 e.stopEvent();
43622             }
43623         };
43624         
43625         this.inputEl().on("keypress", keyPress, this);
43626         
43627     },
43628     
43629     onTriggerClick : function(e)
43630     {   
43631         if(this.disabled){
43632             return;
43633         }
43634         
43635         this.page = 0;
43636         this.loadNext = false;
43637         
43638         if(this.isExpanded()){
43639             this.collapse();
43640             return;
43641         }
43642         
43643         this.hasFocus = true;
43644         
43645         if(this.triggerAction == 'all') {
43646             this.doQuery(this.allQuery, true);
43647             return;
43648         }
43649         
43650         this.doQuery(this.getRawValue());
43651     },
43652     
43653     getCurrency : function()
43654     {   
43655         var v = this.currencyEl().getValue();
43656         
43657         return v;
43658     },
43659     
43660     restrictHeight : function()
43661     {
43662         this.list.alignTo(this.currencyEl(), this.listAlign);
43663         this.list.alignTo(this.currencyEl(), this.listAlign);
43664     },
43665     
43666     onViewClick : function(view, doFocus, el, e)
43667     {
43668         var index = this.view.getSelectedIndexes()[0];
43669         
43670         var r = this.store.getAt(index);
43671         
43672         if(r){
43673             this.onSelect(r, index);
43674         }
43675     },
43676     
43677     onSelect : function(record, index){
43678         
43679         if(this.fireEvent('beforeselect', this, record, index) !== false){
43680         
43681             this.setFromCurrencyData(index > -1 ? record.data : false);
43682             
43683             this.collapse();
43684             
43685             this.fireEvent('select', this, record, index);
43686         }
43687     },
43688     
43689     setFromCurrencyData : function(o)
43690     {
43691         var currency = '';
43692         
43693         this.lastCurrency = o;
43694         
43695         if (this.currencyField) {
43696             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43697         } else {
43698             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43699         }
43700         
43701         this.lastSelectionText = currency;
43702         
43703         //setting default currency
43704         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43705             this.setCurrency(this.defaultCurrency);
43706             return;
43707         }
43708         
43709         this.setCurrency(currency);
43710     },
43711     
43712     setFromData : function(o)
43713     {
43714         var c = {};
43715         
43716         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43717         
43718         this.setFromCurrencyData(c);
43719         
43720         var value = '';
43721         
43722         if (this.name) {
43723             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43724         } else {
43725             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43726         }
43727         
43728         this.setValue(value);
43729         
43730     },
43731     
43732     setCurrency : function(v)
43733     {   
43734         this.currencyValue = v;
43735         
43736         if(this.rendered){
43737             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43738             this.validate();
43739         }
43740     },
43741     
43742     setValue : function(v)
43743     {
43744         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43745         
43746         this.value = v;
43747         
43748         if(this.rendered){
43749             
43750             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43751             
43752             this.inputEl().dom.value = (v == '') ? '' :
43753                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43754             
43755             if(!this.allowZero && v === '0') {
43756                 this.hiddenEl().dom.value = '';
43757                 this.inputEl().dom.value = '';
43758             }
43759             
43760             this.validate();
43761         }
43762     },
43763     
43764     getRawValue : function()
43765     {
43766         var v = this.inputEl().getValue();
43767         
43768         return v;
43769     },
43770     
43771     getValue : function()
43772     {
43773         return this.fixPrecision(this.parseValue(this.getRawValue()));
43774     },
43775     
43776     parseValue : function(value)
43777     {
43778         if(this.thousandsDelimiter) {
43779             value += "";
43780             r = new RegExp(",", "g");
43781             value = value.replace(r, "");
43782         }
43783         
43784         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43785         return isNaN(value) ? '' : value;
43786         
43787     },
43788     
43789     fixPrecision : function(value)
43790     {
43791         if(this.thousandsDelimiter) {
43792             value += "";
43793             r = new RegExp(",", "g");
43794             value = value.replace(r, "");
43795         }
43796         
43797         var nan = isNaN(value);
43798         
43799         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43800             return nan ? '' : value;
43801         }
43802         return parseFloat(value).toFixed(this.decimalPrecision);
43803     },
43804     
43805     decimalPrecisionFcn : function(v)
43806     {
43807         return Math.floor(v);
43808     },
43809     
43810     validateValue : function(value)
43811     {
43812         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43813             return false;
43814         }
43815         
43816         var num = this.parseValue(value);
43817         
43818         if(isNaN(num)){
43819             this.markInvalid(String.format(this.nanText, value));
43820             return false;
43821         }
43822         
43823         if(num < this.minValue){
43824             this.markInvalid(String.format(this.minText, this.minValue));
43825             return false;
43826         }
43827         
43828         if(num > this.maxValue){
43829             this.markInvalid(String.format(this.maxText, this.maxValue));
43830             return false;
43831         }
43832         
43833         return true;
43834     },
43835     
43836     validate : function()
43837     {
43838         if(this.disabled || this.allowBlank){
43839             this.markValid();
43840             return true;
43841         }
43842         
43843         var currency = this.getCurrency();
43844         
43845         if(this.validateValue(this.getRawValue()) && currency.length){
43846             this.markValid();
43847             return true;
43848         }
43849         
43850         this.markInvalid();
43851         return false;
43852     },
43853     
43854     getName: function()
43855     {
43856         return this.name;
43857     },
43858     
43859     beforeBlur : function()
43860     {
43861         if(!this.castInt){
43862             return;
43863         }
43864         
43865         var v = this.parseValue(this.getRawValue());
43866         
43867         if(v || v == 0){
43868             this.setValue(v);
43869         }
43870     },
43871     
43872     onBlur : function()
43873     {
43874         this.beforeBlur();
43875         
43876         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43877             //this.el.removeClass(this.focusClass);
43878         }
43879         
43880         this.hasFocus = false;
43881         
43882         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43883             this.validate();
43884         }
43885         
43886         var v = this.getValue();
43887         
43888         if(String(v) !== String(this.startValue)){
43889             this.fireEvent('change', this, v, this.startValue);
43890         }
43891         
43892         this.fireEvent("blur", this);
43893     },
43894     
43895     inputEl : function()
43896     {
43897         return this.el.select('.roo-money-amount-input', true).first();
43898     },
43899     
43900     currencyEl : function()
43901     {
43902         return this.el.select('.roo-money-currency-input', true).first();
43903     },
43904     
43905     hiddenEl : function()
43906     {
43907         return this.el.select('input.hidden-number-input',true).first();
43908     }
43909     
43910 });/**
43911  * @class Roo.bootstrap.BezierSignature
43912  * @extends Roo.bootstrap.Component
43913  * Bootstrap BezierSignature class
43914  * This script refer to:
43915  *    Title: Signature Pad
43916  *    Author: szimek
43917  *    Availability: https://github.com/szimek/signature_pad
43918  *
43919  * @constructor
43920  * Create a new BezierSignature
43921  * @param {Object} config The config object
43922  */
43923
43924 Roo.bootstrap.BezierSignature = function(config){
43925     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43926     this.addEvents({
43927         "resize" : true
43928     });
43929 };
43930
43931 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43932 {
43933      
43934     curve_data: [],
43935     
43936     is_empty: true,
43937     
43938     mouse_btn_down: true,
43939     
43940     /**
43941      * @cfg {int} canvas height
43942      */
43943     canvas_height: '200px',
43944     
43945     /**
43946      * @cfg {float|function} Radius of a single dot.
43947      */ 
43948     dot_size: false,
43949     
43950     /**
43951      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43952      */
43953     min_width: 0.5,
43954     
43955     /**
43956      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43957      */
43958     max_width: 2.5,
43959     
43960     /**
43961      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43962      */
43963     throttle: 16,
43964     
43965     /**
43966      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43967      */
43968     min_distance: 5,
43969     
43970     /**
43971      * @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.
43972      */
43973     bg_color: 'rgba(0, 0, 0, 0)',
43974     
43975     /**
43976      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43977      */
43978     dot_color: 'black',
43979     
43980     /**
43981      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43982      */ 
43983     velocity_filter_weight: 0.7,
43984     
43985     /**
43986      * @cfg {function} Callback when stroke begin. 
43987      */
43988     onBegin: false,
43989     
43990     /**
43991      * @cfg {function} Callback when stroke end.
43992      */
43993     onEnd: false,
43994     
43995     getAutoCreate : function()
43996     {
43997         var cls = 'roo-signature column';
43998         
43999         if(this.cls){
44000             cls += ' ' + this.cls;
44001         }
44002         
44003         var col_sizes = [
44004             'lg',
44005             'md',
44006             'sm',
44007             'xs'
44008         ];
44009         
44010         for(var i = 0; i < col_sizes.length; i++) {
44011             if(this[col_sizes[i]]) {
44012                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44013             }
44014         }
44015         
44016         var cfg = {
44017             tag: 'div',
44018             cls: cls,
44019             cn: [
44020                 {
44021                     tag: 'div',
44022                     cls: 'roo-signature-body',
44023                     cn: [
44024                         {
44025                             tag: 'canvas',
44026                             cls: 'roo-signature-body-canvas',
44027                             height: this.canvas_height,
44028                             width: this.canvas_width
44029                         }
44030                     ]
44031                 },
44032                 {
44033                     tag: 'input',
44034                     type: 'file',
44035                     style: 'display: none'
44036                 }
44037             ]
44038         };
44039         
44040         return cfg;
44041     },
44042     
44043     initEvents: function() 
44044     {
44045         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44046         
44047         var canvas = this.canvasEl();
44048         
44049         // mouse && touch event swapping...
44050         canvas.dom.style.touchAction = 'none';
44051         canvas.dom.style.msTouchAction = 'none';
44052         
44053         this.mouse_btn_down = false;
44054         canvas.on('mousedown', this._handleMouseDown, this);
44055         canvas.on('mousemove', this._handleMouseMove, this);
44056         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44057         
44058         if (window.PointerEvent) {
44059             canvas.on('pointerdown', this._handleMouseDown, this);
44060             canvas.on('pointermove', this._handleMouseMove, this);
44061             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44062         }
44063         
44064         if ('ontouchstart' in window) {
44065             canvas.on('touchstart', this._handleTouchStart, this);
44066             canvas.on('touchmove', this._handleTouchMove, this);
44067             canvas.on('touchend', this._handleTouchEnd, this);
44068         }
44069         
44070         Roo.EventManager.onWindowResize(this.resize, this, true);
44071         
44072         // file input event
44073         this.fileEl().on('change', this.uploadImage, this);
44074         
44075         this.clear();
44076         
44077         this.resize();
44078     },
44079     
44080     resize: function(){
44081         
44082         var canvas = this.canvasEl().dom;
44083         var ctx = this.canvasElCtx();
44084         var img_data = false;
44085         
44086         if(canvas.width > 0) {
44087             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44088         }
44089         // setting canvas width will clean img data
44090         canvas.width = 0;
44091         
44092         var style = window.getComputedStyle ? 
44093             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44094             
44095         var padding_left = parseInt(style.paddingLeft) || 0;
44096         var padding_right = parseInt(style.paddingRight) || 0;
44097         
44098         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44099         
44100         if(img_data) {
44101             ctx.putImageData(img_data, 0, 0);
44102         }
44103     },
44104     
44105     _handleMouseDown: function(e)
44106     {
44107         if (e.browserEvent.which === 1) {
44108             this.mouse_btn_down = true;
44109             this.strokeBegin(e);
44110         }
44111     },
44112     
44113     _handleMouseMove: function (e)
44114     {
44115         if (this.mouse_btn_down) {
44116             this.strokeMoveUpdate(e);
44117         }
44118     },
44119     
44120     _handleMouseUp: function (e)
44121     {
44122         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44123             this.mouse_btn_down = false;
44124             this.strokeEnd(e);
44125         }
44126     },
44127     
44128     _handleTouchStart: function (e) {
44129         
44130         e.preventDefault();
44131         if (e.browserEvent.targetTouches.length === 1) {
44132             // var touch = e.browserEvent.changedTouches[0];
44133             // this.strokeBegin(touch);
44134             
44135              this.strokeBegin(e); // assume e catching the correct xy...
44136         }
44137     },
44138     
44139     _handleTouchMove: function (e) {
44140         e.preventDefault();
44141         // var touch = event.targetTouches[0];
44142         // _this._strokeMoveUpdate(touch);
44143         this.strokeMoveUpdate(e);
44144     },
44145     
44146     _handleTouchEnd: function (e) {
44147         var wasCanvasTouched = e.target === this.canvasEl().dom;
44148         if (wasCanvasTouched) {
44149             e.preventDefault();
44150             // var touch = event.changedTouches[0];
44151             // _this._strokeEnd(touch);
44152             this.strokeEnd(e);
44153         }
44154     },
44155     
44156     reset: function () {
44157         this._lastPoints = [];
44158         this._lastVelocity = 0;
44159         this._lastWidth = (this.min_width + this.max_width) / 2;
44160         this.canvasElCtx().fillStyle = this.dot_color;
44161     },
44162     
44163     strokeMoveUpdate: function(e)
44164     {
44165         this.strokeUpdate(e);
44166         
44167         if (this.throttle) {
44168             this.throttleStroke(this.strokeUpdate, this.throttle);
44169         }
44170         else {
44171             this.strokeUpdate(e);
44172         }
44173     },
44174     
44175     strokeBegin: function(e)
44176     {
44177         var newPointGroup = {
44178             color: this.dot_color,
44179             points: []
44180         };
44181         
44182         if (typeof this.onBegin === 'function') {
44183             this.onBegin(e);
44184         }
44185         
44186         this.curve_data.push(newPointGroup);
44187         this.reset();
44188         this.strokeUpdate(e);
44189     },
44190     
44191     strokeUpdate: function(e)
44192     {
44193         var rect = this.canvasEl().dom.getBoundingClientRect();
44194         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44195         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44196         var lastPoints = lastPointGroup.points;
44197         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44198         var isLastPointTooClose = lastPoint
44199             ? point.distanceTo(lastPoint) <= this.min_distance
44200             : false;
44201         var color = lastPointGroup.color;
44202         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44203             var curve = this.addPoint(point);
44204             if (!lastPoint) {
44205                 this.drawDot({color: color, point: point});
44206             }
44207             else if (curve) {
44208                 this.drawCurve({color: color, curve: curve});
44209             }
44210             lastPoints.push({
44211                 time: point.time,
44212                 x: point.x,
44213                 y: point.y
44214             });
44215         }
44216     },
44217     
44218     strokeEnd: function(e)
44219     {
44220         this.strokeUpdate(e);
44221         if (typeof this.onEnd === 'function') {
44222             this.onEnd(e);
44223         }
44224     },
44225     
44226     addPoint:  function (point) {
44227         var _lastPoints = this._lastPoints;
44228         _lastPoints.push(point);
44229         if (_lastPoints.length > 2) {
44230             if (_lastPoints.length === 3) {
44231                 _lastPoints.unshift(_lastPoints[0]);
44232             }
44233             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44234             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44235             _lastPoints.shift();
44236             return curve;
44237         }
44238         return null;
44239     },
44240     
44241     calculateCurveWidths: function (startPoint, endPoint) {
44242         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44243             (1 - this.velocity_filter_weight) * this._lastVelocity;
44244
44245         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44246         var widths = {
44247             end: newWidth,
44248             start: this._lastWidth
44249         };
44250         
44251         this._lastVelocity = velocity;
44252         this._lastWidth = newWidth;
44253         return widths;
44254     },
44255     
44256     drawDot: function (_a) {
44257         var color = _a.color, point = _a.point;
44258         var ctx = this.canvasElCtx();
44259         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44260         ctx.beginPath();
44261         this.drawCurveSegment(point.x, point.y, width);
44262         ctx.closePath();
44263         ctx.fillStyle = color;
44264         ctx.fill();
44265     },
44266     
44267     drawCurve: function (_a) {
44268         var color = _a.color, curve = _a.curve;
44269         var ctx = this.canvasElCtx();
44270         var widthDelta = curve.endWidth - curve.startWidth;
44271         var drawSteps = Math.floor(curve.length()) * 2;
44272         ctx.beginPath();
44273         ctx.fillStyle = color;
44274         for (var i = 0; i < drawSteps; i += 1) {
44275         var t = i / drawSteps;
44276         var tt = t * t;
44277         var ttt = tt * t;
44278         var u = 1 - t;
44279         var uu = u * u;
44280         var uuu = uu * u;
44281         var x = uuu * curve.startPoint.x;
44282         x += 3 * uu * t * curve.control1.x;
44283         x += 3 * u * tt * curve.control2.x;
44284         x += ttt * curve.endPoint.x;
44285         var y = uuu * curve.startPoint.y;
44286         y += 3 * uu * t * curve.control1.y;
44287         y += 3 * u * tt * curve.control2.y;
44288         y += ttt * curve.endPoint.y;
44289         var width = curve.startWidth + ttt * widthDelta;
44290         this.drawCurveSegment(x, y, width);
44291         }
44292         ctx.closePath();
44293         ctx.fill();
44294     },
44295     
44296     drawCurveSegment: function (x, y, width) {
44297         var ctx = this.canvasElCtx();
44298         ctx.moveTo(x, y);
44299         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44300         this.is_empty = false;
44301     },
44302     
44303     clear: function()
44304     {
44305         var ctx = this.canvasElCtx();
44306         var canvas = this.canvasEl().dom;
44307         ctx.fillStyle = this.bg_color;
44308         ctx.clearRect(0, 0, canvas.width, canvas.height);
44309         ctx.fillRect(0, 0, canvas.width, canvas.height);
44310         this.curve_data = [];
44311         this.reset();
44312         this.is_empty = true;
44313     },
44314     
44315     fileEl: function()
44316     {
44317         return  this.el.select('input',true).first();
44318     },
44319     
44320     canvasEl: function()
44321     {
44322         return this.el.select('canvas',true).first();
44323     },
44324     
44325     canvasElCtx: function()
44326     {
44327         return this.el.select('canvas',true).first().dom.getContext('2d');
44328     },
44329     
44330     getImage: function(type)
44331     {
44332         if(this.is_empty) {
44333             return false;
44334         }
44335         
44336         // encryption ?
44337         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44338     },
44339     
44340     drawFromImage: function(img_src)
44341     {
44342         var img = new Image();
44343         
44344         img.onload = function(){
44345             this.canvasElCtx().drawImage(img, 0, 0);
44346         }.bind(this);
44347         
44348         img.src = img_src;
44349         
44350         this.is_empty = false;
44351     },
44352     
44353     selectImage: function()
44354     {
44355         this.fileEl().dom.click();
44356     },
44357     
44358     uploadImage: function(e)
44359     {
44360         var reader = new FileReader();
44361         
44362         reader.onload = function(e){
44363             var img = new Image();
44364             img.onload = function(){
44365                 this.reset();
44366                 this.canvasElCtx().drawImage(img, 0, 0);
44367             }.bind(this);
44368             img.src = e.target.result;
44369         }.bind(this);
44370         
44371         reader.readAsDataURL(e.target.files[0]);
44372     },
44373     
44374     // Bezier Point Constructor
44375     Point: (function () {
44376         function Point(x, y, time) {
44377             this.x = x;
44378             this.y = y;
44379             this.time = time || Date.now();
44380         }
44381         Point.prototype.distanceTo = function (start) {
44382             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44383         };
44384         Point.prototype.equals = function (other) {
44385             return this.x === other.x && this.y === other.y && this.time === other.time;
44386         };
44387         Point.prototype.velocityFrom = function (start) {
44388             return this.time !== start.time
44389             ? this.distanceTo(start) / (this.time - start.time)
44390             : 0;
44391         };
44392         return Point;
44393     }()),
44394     
44395     
44396     // Bezier Constructor
44397     Bezier: (function () {
44398         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44399             this.startPoint = startPoint;
44400             this.control2 = control2;
44401             this.control1 = control1;
44402             this.endPoint = endPoint;
44403             this.startWidth = startWidth;
44404             this.endWidth = endWidth;
44405         }
44406         Bezier.fromPoints = function (points, widths, scope) {
44407             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44408             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44409             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44410         };
44411         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44412             var dx1 = s1.x - s2.x;
44413             var dy1 = s1.y - s2.y;
44414             var dx2 = s2.x - s3.x;
44415             var dy2 = s2.y - s3.y;
44416             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44417             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44418             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44419             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44420             var dxm = m1.x - m2.x;
44421             var dym = m1.y - m2.y;
44422             var k = l2 / (l1 + l2);
44423             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44424             var tx = s2.x - cm.x;
44425             var ty = s2.y - cm.y;
44426             return {
44427                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44428                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44429             };
44430         };
44431         Bezier.prototype.length = function () {
44432             var steps = 10;
44433             var length = 0;
44434             var px;
44435             var py;
44436             for (var i = 0; i <= steps; i += 1) {
44437                 var t = i / steps;
44438                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44439                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44440                 if (i > 0) {
44441                     var xdiff = cx - px;
44442                     var ydiff = cy - py;
44443                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44444                 }
44445                 px = cx;
44446                 py = cy;
44447             }
44448             return length;
44449         };
44450         Bezier.prototype.point = function (t, start, c1, c2, end) {
44451             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44452             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44453             + (3.0 * c2 * (1.0 - t) * t * t)
44454             + (end * t * t * t);
44455         };
44456         return Bezier;
44457     }()),
44458     
44459     throttleStroke: function(fn, wait) {
44460       if (wait === void 0) { wait = 250; }
44461       var previous = 0;
44462       var timeout = null;
44463       var result;
44464       var storedContext;
44465       var storedArgs;
44466       var later = function () {
44467           previous = Date.now();
44468           timeout = null;
44469           result = fn.apply(storedContext, storedArgs);
44470           if (!timeout) {
44471               storedContext = null;
44472               storedArgs = [];
44473           }
44474       };
44475       return function wrapper() {
44476           var args = [];
44477           for (var _i = 0; _i < arguments.length; _i++) {
44478               args[_i] = arguments[_i];
44479           }
44480           var now = Date.now();
44481           var remaining = wait - (now - previous);
44482           storedContext = this;
44483           storedArgs = args;
44484           if (remaining <= 0 || remaining > wait) {
44485               if (timeout) {
44486                   clearTimeout(timeout);
44487                   timeout = null;
44488               }
44489               previous = now;
44490               result = fn.apply(storedContext, storedArgs);
44491               if (!timeout) {
44492                   storedContext = null;
44493                   storedArgs = [];
44494               }
44495           }
44496           else if (!timeout) {
44497               timeout = window.setTimeout(later, remaining);
44498           }
44499           return result;
44500       };
44501   }
44502   
44503 });
44504
44505  
44506
44507