roojs-bootstrap.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 = (typeof(cfg.style) == 'undefined' ? this.style : 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     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
366     {
367         return Roo.get(document.body);
368     },
369     
370     /**
371      * Fetch the element to display the tooltip on.
372      * @return {Roo.Element} defaults to this.el
373      */
374     tooltipEl : function()
375     {
376         return this.el;
377     },
378         
379     addxtype  : function(tree,cntr)
380     {
381         var cn = this;
382         
383         cn = Roo.factory(tree);
384         //Roo.log(['addxtype', cn]);
385            
386         cn.parentType = this.xtype; //??
387         cn.parentId = this.id;
388         
389         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390         if (typeof(cn.container_method) == 'string') {
391             cntr = cn.container_method;
392         }
393         
394         
395         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
396         
397         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
398         
399         var build_from_html =  Roo.XComponent.build_from_html;
400           
401         var is_body  = (tree.xtype == 'Body') ;
402           
403         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
404           
405         var self_cntr_el = Roo.get(this[cntr](false));
406         
407         // do not try and build conditional elements 
408         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
409             return false;
410         }
411         
412         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414                 return this.addxtypeChild(tree,cntr, is_body);
415             }
416             
417             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
418                 
419             if(echild){
420                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
421             }
422             
423             Roo.log('skipping render');
424             return cn;
425             
426         }
427         
428         var ret = false;
429         if (!build_from_html) {
430             return false;
431         }
432         
433         // this i think handles overlaying multiple children of the same type
434         // with the sam eelement.. - which might be buggy..
435         while (true) {
436             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437             
438             if (!echild) {
439                 break;
440             }
441             
442             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
443                 break;
444             }
445             
446             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
447         }
448        
449         return ret;
450     },
451     
452     
453     addxtypeChild : function (tree, cntr, is_body)
454     {
455         Roo.debug && Roo.log('addxtypeChild:' + cntr);
456         var cn = this;
457         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
458         
459         
460         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461                     (typeof(tree['flexy:foreach']) != 'undefined');
462           
463     
464         
465         skip_children = false;
466         // render the element if it's not BODY.
467         if (!is_body) {
468             
469             // if parent was disabled, then do not try and create the children..
470             if(!this[cntr](true)){
471                 tree.items = [];
472                 return tree;
473             }
474            
475             cn = Roo.factory(tree);
476            
477             cn.parentType = this.xtype; //??
478             cn.parentId = this.id;
479             
480             var build_from_html =  Roo.XComponent.build_from_html;
481             
482             
483             // does the container contain child eleemnts with 'xtype' attributes.
484             // that match this xtype..
485             // note - when we render we create these as well..
486             // so we should check to see if body has xtype set.
487             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
488                
489                 var self_cntr_el = Roo.get(this[cntr](false));
490                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
491                 if (echild) { 
492                     //Roo.log(Roo.XComponent.build_from_html);
493                     //Roo.log("got echild:");
494                     //Roo.log(echild);
495                 }
496                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497                 // and are not displayed -this causes this to use up the wrong element when matching.
498                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
499                 
500                 
501                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503                   
504                   
505                   
506                     cn.el = echild;
507                   //  Roo.log("GOT");
508                     //echild.dom.removeAttribute('xtype');
509                 } else {
510                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511                     Roo.debug && Roo.log(self_cntr_el);
512                     Roo.debug && Roo.log(echild);
513                     Roo.debug && Roo.log(cn);
514                 }
515             }
516            
517             
518            
519             // if object has flexy:if - then it may or may not be rendered.
520             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
521                 // skip a flexy if element.
522                 Roo.debug && Roo.log('skipping render');
523                 Roo.debug && Roo.log(tree);
524                 if (!cn.el) {
525                     Roo.debug && Roo.log('skipping all children');
526                     skip_children = true;
527                 }
528                 
529              } else {
530                  
531                 // actually if flexy:foreach is found, we really want to create 
532                 // multiple copies here...
533                 //Roo.log('render');
534                 //Roo.log(this[cntr]());
535                 // some elements do not have render methods.. like the layouts...
536                 /*
537                 if(this[cntr](true) === false){
538                     cn.items = [];
539                     return cn;
540                 }
541                 */
542                 cn.render && cn.render(this[cntr](true));
543                 
544              }
545             // then add the element..
546         }
547          
548         // handle the kids..
549         
550         var nitems = [];
551         /*
552         if (typeof (tree.menu) != 'undefined') {
553             tree.menu.parentType = cn.xtype;
554             tree.menu.triggerEl = cn.el;
555             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
556             
557         }
558         */
559         if (!tree.items || !tree.items.length) {
560             cn.items = nitems;
561             //Roo.log(["no children", this]);
562             
563             return cn;
564         }
565          
566         var items = tree.items;
567         delete tree.items;
568         
569         //Roo.log(items.length);
570             // add the items..
571         if (!skip_children) {    
572             for(var i =0;i < items.length;i++) {
573               //  Roo.log(['add child', items[i]]);
574                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575             }
576         }
577         
578         cn.items = nitems;
579         
580         //Roo.log("fire childrenrendered");
581         
582         cn.fireEvent('childrenrendered', this);
583         
584         return cn;
585     },
586     
587     /**
588      * Set the element that will be used to show or hide
589      */
590     setVisibilityEl : function(el)
591     {
592         this.visibilityEl = el;
593     },
594     
595      /**
596      * Get the element that will be used to show or hide
597      */
598     getVisibilityEl : function()
599     {
600         if (typeof(this.visibilityEl) == 'object') {
601             return this.visibilityEl;
602         }
603         
604         if (typeof(this.visibilityEl) == 'string') {
605             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
606         }
607         
608         return this.getEl();
609     },
610     
611     /**
612      * Show a component - removes 'hidden' class
613      */
614     show : function()
615     {
616         if(!this.getVisibilityEl()){
617             return;
618         }
619          
620         this.getVisibilityEl().removeClass(['hidden','d-none']);
621         
622         this.fireEvent('show', this);
623         
624         
625     },
626     /**
627      * Hide a component - adds 'hidden' class
628      */
629     hide: function()
630     {
631         if(!this.getVisibilityEl()){
632             return;
633         }
634         
635         this.getVisibilityEl().addClass(['hidden','d-none']);
636         
637         this.fireEvent('hide', this);
638         
639     }
640 });
641
642  /*
643  * - LGPL
644  *
645  * element
646  * 
647  */
648
649 /**
650  * @class Roo.bootstrap.Element
651  * @extends Roo.bootstrap.Component
652  * Bootstrap Element class
653  * @cfg {String} html contents of the element
654  * @cfg {String} tag tag of the element
655  * @cfg {String} cls class of the element
656  * @cfg {Boolean} preventDefault (true|false) default false
657  * @cfg {Boolean} clickable (true|false) default false
658  * @cfg {String} role default blank - set to button to force cursor pointer
659  
660  * 
661  * @constructor
662  * Create a new Element
663  * @param {Object} config The config object
664  */
665
666 Roo.bootstrap.Element = function(config){
667     Roo.bootstrap.Element.superclass.constructor.call(this, config);
668     
669     this.addEvents({
670         // raw events
671         /**
672          * @event click
673          * When a element is chick
674          * @param {Roo.bootstrap.Element} this
675          * @param {Roo.EventObject} e
676          */
677         "click" : true 
678         
679       
680     });
681 };
682
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
684     
685     tag: 'div',
686     cls: '',
687     html: '',
688     preventDefault: false, 
689     clickable: false,
690     tapedTwice : false,
691     role : false,
692     
693     getAutoCreate : function(){
694         
695         var cfg = {
696             tag: this.tag,
697             // cls: this.cls, double assign in parent class Component.js :: onRender
698             html: this.html
699         };
700         if (this.role !== false) {
701             cfg.role = this.role;
702         }
703         
704         return cfg;
705     },
706     
707     initEvents: function() 
708     {
709         Roo.bootstrap.Element.superclass.initEvents.call(this);
710         
711         if(this.clickable){
712             this.el.on('click', this.onClick, this);
713         }
714         
715         
716     },
717     
718     onClick : function(e)
719     {
720         if(this.preventDefault){
721             e.preventDefault();
722         }
723         
724         this.fireEvent('click', this, e); // why was this double click before?
725     },
726     
727     
728     
729
730     
731     
732     getValue : function()
733     {
734         return this.el.dom.innerHTML;
735     },
736     
737     setValue : function(value)
738     {
739         this.el.dom.innerHTML = value;
740     }
741    
742 });
743
744  
745
746  /*
747  * - LGPL
748  *
749  * dropable area
750  * 
751  */
752
753 /**
754  * @class Roo.bootstrap.DropTarget
755  * @extends Roo.bootstrap.Element
756  * Bootstrap DropTarget class
757  
758  * @cfg {string} name dropable name
759  * 
760  * @constructor
761  * Create a new Dropable Area
762  * @param {Object} config The config object
763  */
764
765 Roo.bootstrap.DropTarget = function(config){
766     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
767     
768     this.addEvents({
769         // raw events
770         /**
771          * @event click
772          * When a element is chick
773          * @param {Roo.bootstrap.Element} this
774          * @param {Roo.EventObject} e
775          */
776         "drop" : true
777     });
778 };
779
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
781     
782     
783     getAutoCreate : function(){
784         
785          
786     },
787     
788     initEvents: function() 
789     {
790         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
792             ddGroup: this.name,
793             listeners : {
794                 drop : this.dragDrop.createDelegate(this),
795                 enter : this.dragEnter.createDelegate(this),
796                 out : this.dragOut.createDelegate(this),
797                 over : this.dragOver.createDelegate(this)
798             }
799             
800         });
801         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
802     },
803     
804     dragDrop : function(source,e,data)
805     {
806         // user has to decide how to impliment this.
807         Roo.log('drop');
808         Roo.log(this);
809         //this.fireEvent('drop', this, source, e ,data);
810         return false;
811     },
812     
813     dragEnter : function(n, dd, e, data)
814     {
815         // probably want to resize the element to match the dropped element..
816         Roo.log("enter");
817         this.originalSize = this.el.getSize();
818         this.el.setSize( n.el.getSize());
819         this.dropZone.DDM.refreshCache(this.name);
820         Roo.log([n, dd, e, data]);
821     },
822     
823     dragOut : function(value)
824     {
825         // resize back to normal
826         Roo.log("out");
827         this.el.setSize(this.originalSize);
828         this.dropZone.resetConstraints();
829     },
830     
831     dragOver : function()
832     {
833         // ??? do nothing?
834     }
835    
836 });
837
838  
839
840  /*
841  * - LGPL
842  *
843  * Body
844  *
845  */
846
847 /**
848  * @class Roo.bootstrap.Body
849  * @extends Roo.bootstrap.Component
850  * Bootstrap Body class
851  *
852  * @constructor
853  * Create a new body
854  * @param {Object} config The config object
855  */
856
857 Roo.bootstrap.Body = function(config){
858
859     config = config || {};
860
861     Roo.bootstrap.Body.superclass.constructor.call(this, config);
862     this.el = Roo.get(config.el ? config.el : document.body );
863     if (this.cls && this.cls.length) {
864         Roo.get(document.body).addClass(this.cls);
865     }
866 };
867
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
869
870     is_body : true,// just to make sure it's constructed?
871
872         autoCreate : {
873         cls: 'container'
874     },
875     onRender : function(ct, position)
876     {
877        /* Roo.log("Roo.bootstrap.Body - onRender");
878         if (this.cls && this.cls.length) {
879             Roo.get(document.body).addClass(this.cls);
880         }
881         // style??? xttr???
882         */
883     }
884
885
886
887
888 });
889 /*
890  * - LGPL
891  *
892  * button group
893  * 
894  */
895
896
897 /**
898  * @class Roo.bootstrap.ButtonGroup
899  * @extends Roo.bootstrap.Component
900  * Bootstrap ButtonGroup class
901  * @cfg {String} size lg | sm | xs (default empty normal)
902  * @cfg {String} align vertical | justified  (default none)
903  * @cfg {String} direction up | down (default down)
904  * @cfg {Boolean} toolbar false | true
905  * @cfg {Boolean} btn true | false
906  * 
907  * 
908  * @constructor
909  * Create a new Input
910  * @param {Object} config The config object
911  */
912
913 Roo.bootstrap.ButtonGroup = function(config){
914     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
915 };
916
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
918     
919     size: '',
920     align: '',
921     direction: '',
922     toolbar: false,
923     btn: true,
924
925     getAutoCreate : function(){
926         var cfg = {
927             cls: 'btn-group',
928             html : null
929         };
930         
931         cfg.html = this.html || cfg.html;
932         
933         if (this.toolbar) {
934             cfg = {
935                 cls: 'btn-toolbar',
936                 html: null
937             };
938             
939             return cfg;
940         }
941         
942         if (['vertical','justified'].indexOf(this.align)!==-1) {
943             cfg.cls = 'btn-group-' + this.align;
944             
945             if (this.align == 'justified') {
946                 console.log(this.items);
947             }
948         }
949         
950         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951             cfg.cls += ' btn-group-' + this.size;
952         }
953         
954         if (this.direction == 'up') {
955             cfg.cls += ' dropup' ;
956         }
957         
958         return cfg;
959     },
960     /**
961      * Add a button to the group (similar to NavItem API.)
962      */
963     addItem : function(cfg)
964     {
965         var cn = new Roo.bootstrap.Button(cfg);
966         //this.register(cn);
967         cn.parentId = this.id;
968         cn.onRender(this.el, null);
969         return cn;
970     }
971    
972 });
973
974  /*
975  * - LGPL
976  *
977  * button
978  * 
979  */
980
981 /**
982  * @class Roo.bootstrap.Button
983  * @extends Roo.bootstrap.Component
984  * Bootstrap Button class
985  * @cfg {String} html The button content
986  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989  * @cfg {String} size (lg|sm|xs)
990  * @cfg {String} tag (a|input|submit)
991  * @cfg {String} href empty or href
992  * @cfg {Boolean} disabled default false;
993  * @cfg {Boolean} isClose default false;
994  * @cfg {String} glyphicon depricated - use fa
995  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996  * @cfg {String} badge text for badge
997  * @cfg {String} theme (default|glow)  
998  * @cfg {Boolean} inverse dark themed version
999  * @cfg {Boolean} toggle is it a slidy toggle button
1000  * @cfg {Boolean} pressed   default null - if the button ahs active state
1001  * @cfg {String} ontext text for on slidy toggle state
1002  * @cfg {String} offtext text for off slidy toggle state
1003  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1004  * @cfg {Boolean} removeClass remove the standard class..
1005  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1006  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1007  * 
1008  * @constructor
1009  * Create a new button
1010  * @param {Object} config The config object
1011  */
1012
1013
1014 Roo.bootstrap.Button = function(config){
1015     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1016     
1017     this.addEvents({
1018         // raw events
1019         /**
1020          * @event click
1021          * When a button is pressed
1022          * @param {Roo.bootstrap.Button} btn
1023          * @param {Roo.EventObject} e
1024          */
1025         "click" : true,
1026         /**
1027          * @event dblclick
1028          * When a button is double clicked
1029          * @param {Roo.bootstrap.Button} btn
1030          * @param {Roo.EventObject} e
1031          */
1032         "dblclick" : true,
1033          /**
1034          * @event toggle
1035          * After the button has been toggles
1036          * @param {Roo.bootstrap.Button} btn
1037          * @param {Roo.EventObject} e
1038          * @param {boolean} pressed (also available as button.pressed)
1039          */
1040         "toggle" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1045     html: false,
1046     active: false,
1047     weight: '',
1048     badge_weight: '',
1049     outline : false,
1050     size: '',
1051     tag: 'button',
1052     href: '',
1053     disabled: false,
1054     isClose: false,
1055     glyphicon: '',
1056     fa: '',
1057     badge: '',
1058     theme: 'default',
1059     inverse: false,
1060     
1061     toggle: false,
1062     ontext: 'ON',
1063     offtext: 'OFF',
1064     defaulton: true,
1065     preventDefault: true,
1066     removeClass: false,
1067     name: false,
1068     target: false,
1069     group : false,
1070      
1071     pressed : null,
1072      
1073     
1074     getAutoCreate : function(){
1075         
1076         var cfg = {
1077             tag : 'button',
1078             cls : 'roo-button',
1079             html: ''
1080         };
1081         
1082         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084             this.tag = 'button';
1085         } else {
1086             cfg.tag = this.tag;
1087         }
1088         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1089         
1090         if (this.toggle == true) {
1091             cfg={
1092                 tag: 'div',
1093                 cls: 'slider-frame roo-button',
1094                 cn: [
1095                     {
1096                         tag: 'span',
1097                         'data-on-text':'ON',
1098                         'data-off-text':'OFF',
1099                         cls: 'slider-button',
1100                         html: this.offtext
1101                     }
1102                 ]
1103             };
1104             // why are we validating the weights?
1105             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106                 cfg.cls +=  ' ' + this.weight;
1107             }
1108             
1109             return cfg;
1110         }
1111         
1112         if (this.isClose) {
1113             cfg.cls += ' close';
1114             
1115             cfg["aria-hidden"] = true;
1116             
1117             cfg.html = "&times;";
1118             
1119             return cfg;
1120         }
1121              
1122         
1123         if (this.theme==='default') {
1124             cfg.cls = 'btn roo-button';
1125             
1126             //if (this.parentType != 'Navbar') {
1127             this.weight = this.weight.length ?  this.weight : 'default';
1128             //}
1129             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1130                 
1131                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133                 cfg.cls += ' btn-' + outline + weight;
1134                 if (this.weight == 'default') {
1135                     // BC
1136                     cfg.cls += ' btn-' + this.weight;
1137                 }
1138             }
1139         } else if (this.theme==='glow') {
1140             
1141             cfg.tag = 'a';
1142             cfg.cls = 'btn-glow roo-button';
1143             
1144             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1145                 
1146                 cfg.cls += ' ' + this.weight;
1147             }
1148         }
1149    
1150         
1151         if (this.inverse) {
1152             this.cls += ' inverse';
1153         }
1154         
1155         
1156         if (this.active || this.pressed === true) {
1157             cfg.cls += ' active';
1158         }
1159         
1160         if (this.disabled) {
1161             cfg.disabled = 'disabled';
1162         }
1163         
1164         if (this.items) {
1165             Roo.log('changing to ul' );
1166             cfg.tag = 'ul';
1167             this.glyphicon = 'caret';
1168             if (Roo.bootstrap.version == 4) {
1169                 this.fa = 'caret-down';
1170             }
1171             
1172         }
1173         
1174         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1175          
1176         //gsRoo.log(this.parentType);
1177         if (this.parentType === 'Navbar' && !this.parent().bar) {
1178             Roo.log('changing to li?');
1179             
1180             cfg.tag = 'li';
1181             
1182             cfg.cls = '';
1183             cfg.cn =  [{
1184                 tag : 'a',
1185                 cls : 'roo-button',
1186                 html : this.html,
1187                 href : this.href || '#'
1188             }];
1189             if (this.menu) {
1190                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1191                 cfg.cls += ' dropdown';
1192             }   
1193             
1194             delete cfg.html;
1195             
1196         }
1197         
1198        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1199         
1200         if (this.glyphicon) {
1201             cfg.html = ' ' + cfg.html;
1202             
1203             cfg.cn = [
1204                 {
1205                     tag: 'span',
1206                     cls: 'glyphicon glyphicon-' + this.glyphicon
1207                 }
1208             ];
1209         }
1210         if (this.fa) {
1211             cfg.html = ' ' + cfg.html;
1212             
1213             cfg.cn = [
1214                 {
1215                     tag: 'i',
1216                     cls: 'fa fas fa-' + this.fa
1217                 }
1218             ];
1219         }
1220         
1221         if (this.badge) {
1222             cfg.html += ' ';
1223             
1224             cfg.tag = 'a';
1225             
1226 //            cfg.cls='btn roo-button';
1227             
1228             cfg.href=this.href;
1229             
1230             var value = cfg.html;
1231             
1232             if(this.glyphicon){
1233                 value = {
1234                     tag: 'span',
1235                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1236                     html: this.html
1237                 };
1238             }
1239             if(this.fa){
1240                 value = {
1241                     tag: 'i',
1242                     cls: 'fa fas fa-' + this.fa,
1243                     html: this.html
1244                 };
1245             }
1246             
1247             var bw = this.badge_weight.length ? this.badge_weight :
1248                 (this.weight.length ? this.weight : 'secondary');
1249             bw = bw == 'default' ? 'secondary' : bw;
1250             
1251             cfg.cn = [
1252                 value,
1253                 {
1254                     tag: 'span',
1255                     cls: 'badge badge-' + bw,
1256                     html: this.badge
1257                 }
1258             ];
1259             
1260             cfg.html='';
1261         }
1262         
1263         if (this.menu) {
1264             cfg.cls += ' dropdown';
1265             cfg.html = typeof(cfg.html) != 'undefined' ?
1266                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1267         }
1268         
1269         if (cfg.tag !== 'a' && this.href !== '') {
1270             throw "Tag must be a to set href.";
1271         } else if (this.href.length > 0) {
1272             cfg.href = this.href;
1273         }
1274         
1275         if(this.removeClass){
1276             cfg.cls = '';
1277         }
1278         
1279         if(this.target){
1280             cfg.target = this.target;
1281         }
1282         
1283         return cfg;
1284     },
1285     initEvents: function() {
1286        // Roo.log('init events?');
1287 //        Roo.log(this.el.dom);
1288         // add the menu...
1289         
1290         if (typeof (this.menu) != 'undefined') {
1291             this.menu.parentType = this.xtype;
1292             this.menu.triggerEl = this.el;
1293             this.addxtype(Roo.apply({}, this.menu));
1294         }
1295
1296
1297         if (this.el.hasClass('roo-button')) {
1298              this.el.on('click', this.onClick, this);
1299              this.el.on('dblclick', this.onDblClick, this);
1300         } else {
1301              this.el.select('.roo-button').on('click', this.onClick, this);
1302              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1303              
1304         }
1305         // why?
1306         if(this.removeClass){
1307             this.el.on('click', this.onClick, this);
1308         }
1309         
1310         if (this.group === true) {
1311              if (this.pressed === false || this.pressed === true) {
1312                 // nothing
1313             } else {
1314                 this.pressed = false;
1315                 this.setActive(this.pressed);
1316             }
1317             
1318         }
1319         
1320         this.el.enableDisplayMode();
1321         
1322     },
1323     onClick : function(e)
1324     {
1325         if (this.disabled) {
1326             return;
1327         }
1328         
1329         Roo.log('button on click ');
1330         if(this.preventDefault){
1331             e.preventDefault();
1332         }
1333         
1334         if (this.group) {
1335             if (this.pressed) {
1336                 // do nothing -
1337                 return;
1338             }
1339             this.setActive(true);
1340             var pi = this.parent().items;
1341             for (var i = 0;i < pi.length;i++) {
1342                 if (this == pi[i]) {
1343                     continue;
1344                 }
1345                 if (pi[i].el.hasClass('roo-button')) {
1346                     pi[i].setActive(false);
1347                 }
1348             }
1349             this.fireEvent('click', this, e);            
1350             return;
1351         }
1352         
1353         if (this.pressed === true || this.pressed === false) {
1354             this.toggleActive(e);
1355         }
1356         
1357         
1358         this.fireEvent('click', this, e);
1359     },
1360     onDblClick: function(e)
1361     {
1362         if (this.disabled) {
1363             return;
1364         }
1365         if(this.preventDefault){
1366             e.preventDefault();
1367         }
1368         this.fireEvent('dblclick', this, e);
1369     },
1370     /**
1371      * Enables this button
1372      */
1373     enable : function()
1374     {
1375         this.disabled = false;
1376         this.el.removeClass('disabled');
1377         this.el.dom.removeAttribute("disabled");
1378     },
1379     
1380     /**
1381      * Disable this button
1382      */
1383     disable : function()
1384     {
1385         this.disabled = true;
1386         this.el.addClass('disabled');
1387         this.el.attr("disabled", "disabled")
1388     },
1389      /**
1390      * sets the active state on/off, 
1391      * @param {Boolean} state (optional) Force a particular state
1392      */
1393     setActive : function(v) {
1394         
1395         this.el[v ? 'addClass' : 'removeClass']('active');
1396         this.pressed = v;
1397     },
1398      /**
1399      * toggles the current active state 
1400      */
1401     toggleActive : function(e)
1402     {
1403         this.setActive(!this.pressed); // this modifies pressed...
1404         this.fireEvent('toggle', this, e, this.pressed);
1405     },
1406      /**
1407      * get the current active state
1408      * @return {boolean} true if it's active
1409      */
1410     isActive : function()
1411     {
1412         return this.el.hasClass('active');
1413     },
1414     /**
1415      * set the text of the first selected button
1416      */
1417     setText : function(str)
1418     {
1419         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1420     },
1421     /**
1422      * get the text of the first selected button
1423      */
1424     getText : function()
1425     {
1426         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1427     },
1428     
1429     setWeight : function(str)
1430     {
1431         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1433         this.weight = str;
1434         var outline = this.outline ? 'outline-' : '';
1435         if (str == 'default') {
1436             this.el.addClass('btn-default btn-outline-secondary');        
1437             return;
1438         }
1439         this.el.addClass('btn-' + outline + str);        
1440     }
1441     
1442     
1443 });
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1445
1446 Roo.bootstrap.Button.weights = [
1447     'default',
1448     'secondary' ,
1449     'primary',
1450     'success',
1451     'info',
1452     'warning',
1453     'danger',
1454     'link',
1455     'light',
1456     'dark'              
1457    
1458 ];/*
1459  * - LGPL
1460  *
1461  * column
1462  * 
1463  */
1464
1465 /**
1466  * @class Roo.bootstrap.Column
1467  * @extends Roo.bootstrap.Component
1468  * Bootstrap Column class
1469  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1477  *
1478  * 
1479  * @cfg {Boolean} hidden (true|false) hide the element
1480  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481  * @cfg {String} fa (ban|check|...) font awesome icon
1482  * @cfg {Number} fasize (1|2|....) font awsome size
1483
1484  * @cfg {String} icon (info-sign|check|...) glyphicon name
1485
1486  * @cfg {String} html content of column.
1487  * 
1488  * @constructor
1489  * Create a new Column
1490  * @param {Object} config The config object
1491  */
1492
1493 Roo.bootstrap.Column = function(config){
1494     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1495 };
1496
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1498     
1499     xs: false,
1500     sm: false,
1501     md: false,
1502     lg: false,
1503     xsoff: false,
1504     smoff: false,
1505     mdoff: false,
1506     lgoff: false,
1507     html: '',
1508     offset: 0,
1509     alert: false,
1510     fa: false,
1511     icon : false,
1512     hidden : false,
1513     fasize : 1,
1514     
1515     getAutoCreate : function(){
1516         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1517         
1518         cfg = {
1519             tag: 'div',
1520             cls: 'column'
1521         };
1522         
1523         var settings=this;
1524         var sizes =   ['xs','sm','md','lg'];
1525         sizes.map(function(size ,ix){
1526             //Roo.log( size + ':' + settings[size]);
1527             
1528             if (settings[size+'off'] !== false) {
1529                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1530             }
1531             
1532             if (settings[size] === false) {
1533                 return;
1534             }
1535             
1536             if (!settings[size]) { // 0 = hidden
1537                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1538                 // bootsrap4
1539                 for (var i = ix; i > -1; i--) {
1540                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1541                 }
1542                 
1543                 
1544                 return;
1545             }
1546             cfg.cls += ' col-' + size + '-' + settings[size] + (
1547                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1548             );
1549             
1550         });
1551         
1552         if (this.hidden) {
1553             cfg.cls += ' hidden';
1554         }
1555         
1556         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557             cfg.cls +=' alert alert-' + this.alert;
1558         }
1559         
1560         
1561         if (this.html.length) {
1562             cfg.html = this.html;
1563         }
1564         if (this.fa) {
1565             var fasize = '';
1566             if (this.fasize > 1) {
1567                 fasize = ' fa-' + this.fasize + 'x';
1568             }
1569             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1570             
1571             
1572         }
1573         if (this.icon) {
1574             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1575         }
1576         
1577         return cfg;
1578     }
1579    
1580 });
1581
1582  
1583
1584  /*
1585  * - LGPL
1586  *
1587  * page container.
1588  * 
1589  */
1590
1591
1592 /**
1593  * @class Roo.bootstrap.Container
1594  * @extends Roo.bootstrap.Component
1595  * Bootstrap Container class
1596  * @cfg {Boolean} jumbotron is it a jumbotron element
1597  * @cfg {String} html content of element
1598  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1600  * @cfg {String} header content of header (for panel)
1601  * @cfg {String} footer content of footer (for panel)
1602  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603  * @cfg {String} tag (header|aside|section) type of HTML tag.
1604  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605  * @cfg {String} fa font awesome icon
1606  * @cfg {String} icon (info-sign|check|...) glyphicon name
1607  * @cfg {Boolean} hidden (true|false) hide the element
1608  * @cfg {Boolean} expandable (true|false) default false
1609  * @cfg {Boolean} expanded (true|false) default true
1610  * @cfg {String} rheader contet on the right of header
1611  * @cfg {Boolean} clickable (true|false) default false
1612
1613  *     
1614  * @constructor
1615  * Create a new Container
1616  * @param {Object} config The config object
1617  */
1618
1619 Roo.bootstrap.Container = function(config){
1620     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1621     
1622     this.addEvents({
1623         // raw events
1624          /**
1625          * @event expand
1626          * After the panel has been expand
1627          * 
1628          * @param {Roo.bootstrap.Container} this
1629          */
1630         "expand" : true,
1631         /**
1632          * @event collapse
1633          * After the panel has been collapsed
1634          * 
1635          * @param {Roo.bootstrap.Container} this
1636          */
1637         "collapse" : true,
1638         /**
1639          * @event click
1640          * When a element is chick
1641          * @param {Roo.bootstrap.Container} this
1642          * @param {Roo.EventObject} e
1643          */
1644         "click" : true
1645     });
1646 };
1647
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1649     
1650     jumbotron : false,
1651     well: '',
1652     panel : '',
1653     header: '',
1654     footer : '',
1655     sticky: '',
1656     tag : false,
1657     alert : false,
1658     fa: false,
1659     icon : false,
1660     expandable : false,
1661     rheader : '',
1662     expanded : true,
1663     clickable: false,
1664   
1665      
1666     getChildContainer : function() {
1667         
1668         if(!this.el){
1669             return false;
1670         }
1671         
1672         if (this.panel.length) {
1673             return this.el.select('.panel-body',true).first();
1674         }
1675         
1676         return this.el;
1677     },
1678     
1679     
1680     getAutoCreate : function(){
1681         
1682         var cfg = {
1683             tag : this.tag || 'div',
1684             html : '',
1685             cls : ''
1686         };
1687         if (this.jumbotron) {
1688             cfg.cls = 'jumbotron';
1689         }
1690         
1691         
1692         
1693         // - this is applied by the parent..
1694         //if (this.cls) {
1695         //    cfg.cls = this.cls + '';
1696         //}
1697         
1698         if (this.sticky.length) {
1699             
1700             var bd = Roo.get(document.body);
1701             if (!bd.hasClass('bootstrap-sticky')) {
1702                 bd.addClass('bootstrap-sticky');
1703                 Roo.select('html',true).setStyle('height', '100%');
1704             }
1705              
1706             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1707         }
1708         
1709         
1710         if (this.well.length) {
1711             switch (this.well) {
1712                 case 'lg':
1713                 case 'sm':
1714                     cfg.cls +=' well well-' +this.well;
1715                     break;
1716                 default:
1717                     cfg.cls +=' well';
1718                     break;
1719             }
1720         }
1721         
1722         if (this.hidden) {
1723             cfg.cls += ' hidden';
1724         }
1725         
1726         
1727         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728             cfg.cls +=' alert alert-' + this.alert;
1729         }
1730         
1731         var body = cfg;
1732         
1733         if (this.panel.length) {
1734             cfg.cls += ' panel panel-' + this.panel;
1735             cfg.cn = [];
1736             if (this.header.length) {
1737                 
1738                 var h = [];
1739                 
1740                 if(this.expandable){
1741                     
1742                     cfg.cls = cfg.cls + ' expandable';
1743                     
1744                     h.push({
1745                         tag: 'i',
1746                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1747                     });
1748                     
1749                 }
1750                 
1751                 h.push(
1752                     {
1753                         tag: 'span',
1754                         cls : 'panel-title',
1755                         html : (this.expandable ? '&nbsp;' : '') + this.header
1756                     },
1757                     {
1758                         tag: 'span',
1759                         cls: 'panel-header-right',
1760                         html: this.rheader
1761                     }
1762                 );
1763                 
1764                 cfg.cn.push({
1765                     cls : 'panel-heading',
1766                     style : this.expandable ? 'cursor: pointer' : '',
1767                     cn : h
1768                 });
1769                 
1770             }
1771             
1772             body = false;
1773             cfg.cn.push({
1774                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1775                 html : this.html
1776             });
1777             
1778             
1779             if (this.footer.length) {
1780                 cfg.cn.push({
1781                     cls : 'panel-footer',
1782                     html : this.footer
1783                     
1784                 });
1785             }
1786             
1787         }
1788         
1789         if (body) {
1790             body.html = this.html || cfg.html;
1791             // prefix with the icons..
1792             if (this.fa) {
1793                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1794             }
1795             if (this.icon) {
1796                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1797             }
1798             
1799             
1800         }
1801         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802             cfg.cls =  'container';
1803         }
1804         
1805         return cfg;
1806     },
1807     
1808     initEvents: function() 
1809     {
1810         if(this.expandable){
1811             var headerEl = this.headerEl();
1812         
1813             if(headerEl){
1814                 headerEl.on('click', this.onToggleClick, this);
1815             }
1816         }
1817         
1818         if(this.clickable){
1819             this.el.on('click', this.onClick, this);
1820         }
1821         
1822     },
1823     
1824     onToggleClick : function()
1825     {
1826         var headerEl = this.headerEl();
1827         
1828         if(!headerEl){
1829             return;
1830         }
1831         
1832         if(this.expanded){
1833             this.collapse();
1834             return;
1835         }
1836         
1837         this.expand();
1838     },
1839     
1840     expand : function()
1841     {
1842         if(this.fireEvent('expand', this)) {
1843             
1844             this.expanded = true;
1845             
1846             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1847             
1848             this.el.select('.panel-body',true).first().removeClass('hide');
1849             
1850             var toggleEl = this.toggleEl();
1851
1852             if(!toggleEl){
1853                 return;
1854             }
1855
1856             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1857         }
1858         
1859     },
1860     
1861     collapse : function()
1862     {
1863         if(this.fireEvent('collapse', this)) {
1864             
1865             this.expanded = false;
1866             
1867             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868             this.el.select('.panel-body',true).first().addClass('hide');
1869         
1870             var toggleEl = this.toggleEl();
1871
1872             if(!toggleEl){
1873                 return;
1874             }
1875
1876             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1877         }
1878     },
1879     
1880     toggleEl : function()
1881     {
1882         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1883             return;
1884         }
1885         
1886         return this.el.select('.panel-heading .fa',true).first();
1887     },
1888     
1889     headerEl : function()
1890     {
1891         if(!this.el || !this.panel.length || !this.header.length){
1892             return;
1893         }
1894         
1895         return this.el.select('.panel-heading',true).first()
1896     },
1897     
1898     bodyEl : function()
1899     {
1900         if(!this.el || !this.panel.length){
1901             return;
1902         }
1903         
1904         return this.el.select('.panel-body',true).first()
1905     },
1906     
1907     titleEl : function()
1908     {
1909         if(!this.el || !this.panel.length || !this.header.length){
1910             return;
1911         }
1912         
1913         return this.el.select('.panel-title',true).first();
1914     },
1915     
1916     setTitle : function(v)
1917     {
1918         var titleEl = this.titleEl();
1919         
1920         if(!titleEl){
1921             return;
1922         }
1923         
1924         titleEl.dom.innerHTML = v;
1925     },
1926     
1927     getTitle : function()
1928     {
1929         
1930         var titleEl = this.titleEl();
1931         
1932         if(!titleEl){
1933             return '';
1934         }
1935         
1936         return titleEl.dom.innerHTML;
1937     },
1938     
1939     setRightTitle : function(v)
1940     {
1941         var t = this.el.select('.panel-header-right',true).first();
1942         
1943         if(!t){
1944             return;
1945         }
1946         
1947         t.dom.innerHTML = v;
1948     },
1949     
1950     onClick : function(e)
1951     {
1952         e.preventDefault();
1953         
1954         this.fireEvent('click', this, e);
1955     }
1956 });
1957
1958  /*
1959  *  - LGPL
1960  *
1961  *  This is BS4's Card element.. - similar to our containers probably..
1962  * 
1963  */
1964 /**
1965  * @class Roo.bootstrap.Card
1966  * @extends Roo.bootstrap.Component
1967  * Bootstrap Card class
1968  *
1969  *
1970  * possible... may not be implemented..
1971  * @cfg {String} header_image  src url of image.
1972  * @cfg {String|Object} header
1973  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1975  * 
1976  * @cfg {String} title
1977  * @cfg {String} subtitle
1978  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979  * @cfg {String} footer
1980  
1981  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1982  * 
1983  * @cfg {String} margin (0|1|2|3|4|5|auto)
1984  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1990  *
1991  * @cfg {String} padding (0|1|2|3|4|5)
1992  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994  * @cfg {String} padding_left (0|1|2|3|4|5)
1995  * @cfg {String} padding_right (0|1|2|3|4|5)
1996  * @cfg {String} padding_x (0|1|2|3|4|5)
1997  * @cfg {String} padding_y (0|1|2|3|4|5)
1998  *
1999  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  
2005  * @config {Boolean} dragable  if this card can be dragged.
2006  * @config {String} drag_group  group for drag
2007  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2008  * @config {String} drop_group  group for drag
2009  * 
2010  * @config {Boolean} collapsable can the body be collapsed.
2011  * @config {Boolean} collapsed is the body collapsed when rendered...
2012  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013  * @config {Boolean} rotated is the body rotated when rendered...
2014  * 
2015  * @constructor
2016  * Create a new Container
2017  * @param {Object} config The config object
2018  */
2019
2020 Roo.bootstrap.Card = function(config){
2021     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2022     
2023     this.addEvents({
2024          // raw events
2025         /**
2026          * @event drop
2027          * When a element a card is dropped
2028          * @param {Roo.bootstrap.Card} this
2029          *
2030          * 
2031          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032          * @param {String} position 'above' or 'below'
2033          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2034         
2035          */
2036         'drop' : true,
2037          /**
2038          * @event rotate
2039          * When a element a card is rotate
2040          * @param {Roo.bootstrap.Card} this
2041          * @param {Roo.Element} n the node being dropped?
2042          * @param {Boolean} rotate status
2043          */
2044         'rotate' : true,
2045         /**
2046          * @event cardover
2047          * When a card element is dragged over ready to drop (return false to block dropable)
2048          * @param {Roo.bootstrap.Card} this
2049          * @param {Object} data from dragdrop 
2050          */
2051          'cardover' : true
2052          
2053     });
2054 };
2055
2056
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2058     
2059     
2060     weight : '',
2061     
2062     margin: '', /// may be better in component?
2063     margin_top: '', 
2064     margin_bottom: '', 
2065     margin_left: '',
2066     margin_right: '',
2067     margin_x: '',
2068     margin_y: '',
2069     
2070     padding : '',
2071     padding_top: '', 
2072     padding_bottom: '', 
2073     padding_left: '',
2074     padding_right: '',
2075     padding_x: '',
2076     padding_y: '',
2077     
2078     display: '', 
2079     display_xs: '', 
2080     display_sm: '', 
2081     display_lg: '',
2082     display_xl: '',
2083  
2084     header_image  : '',
2085     header : '',
2086     header_size : 0,
2087     title : '',
2088     subtitle : '',
2089     html : '',
2090     footer: '',
2091
2092     collapsable : false,
2093     collapsed : false,
2094     rotateable : false,
2095     rotated : false,
2096     
2097     dragable : false,
2098     drag_group : false,
2099     dropable : false,
2100     drop_group : false,
2101     childContainer : false,
2102     dropEl : false, /// the dom placeholde element that indicates drop location.
2103     containerEl: false, // body container
2104     bodyEl: false, // card-body
2105     headerContainerEl : false, //
2106     headerEl : false,
2107     header_imageEl : false,
2108     
2109     
2110     layoutCls : function()
2111     {
2112         var cls = '';
2113         var t = this;
2114         Roo.log(this.margin_bottom.length);
2115         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2117             
2118             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2120             }
2121             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2123             }
2124         });
2125         
2126         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2129             }
2130         });
2131         
2132         // more generic support?
2133         if (this.hidden) {
2134             cls += ' d-none';
2135         }
2136         
2137         return cls;
2138     },
2139  
2140        // Roo.log("Call onRender: " + this.xtype);
2141         /*  We are looking at something like this.
2142 <div class="card">
2143     <img src="..." class="card-img-top" alt="...">
2144     <div class="card-body">
2145         <h5 class="card-title">Card title</h5>
2146          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2147
2148         >> this bit is really the body...
2149         <div> << we will ad dthis in hopefully it will not break shit.
2150         
2151         ** card text does not actually have any styling...
2152         
2153             <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>
2154         
2155         </div> <<
2156           <a href="#" class="card-link">Card link</a>
2157           
2158     </div>
2159     <div class="card-footer">
2160         <small class="text-muted">Last updated 3 mins ago</small>
2161     </div>
2162 </div>
2163          */
2164     getAutoCreate : function(){
2165         
2166         var cfg = {
2167             tag : 'div',
2168             cls : 'card',
2169             cn : [ ]
2170         };
2171         
2172         if (this.weight.length && this.weight != 'light') {
2173             cfg.cls += ' text-white';
2174         } else {
2175             cfg.cls += ' text-dark'; // need as it's nested..
2176         }
2177         if (this.weight.length) {
2178             cfg.cls += ' bg-' + this.weight;
2179         }
2180         
2181         cfg.cls += ' ' + this.layoutCls(); 
2182         
2183         var hdr = false;
2184         var hdr_ctr = false;
2185         if (this.header.length) {
2186             hdr = {
2187                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2189                 cn : []
2190             };
2191             cfg.cn.push(hdr);
2192             hdr_ctr = hdr;
2193         } else {
2194             hdr = {
2195                 tag : 'div',
2196                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2197                 cn : []
2198             };
2199             cfg.cn.push(hdr);
2200             hdr_ctr = hdr;
2201         }
2202         if (this.collapsable) {
2203             hdr_ctr = {
2204             tag : 'a',
2205             cls : 'd-block user-select-none',
2206             cn: [
2207                     {
2208                         tag: 'i',
2209                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2210                     }
2211                    
2212                 ]
2213             };
2214             hdr.cn.push(hdr_ctr);
2215         }
2216         
2217         hdr_ctr.cn.push(        {
2218             tag: 'span',
2219             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2220             html : this.header
2221         });
2222         
2223         
2224         if (this.header_image.length) {
2225             cfg.cn.push({
2226                 tag : 'img',
2227                 cls : 'card-img-top',
2228                 src: this.header_image // escape?
2229             });
2230         } else {
2231             cfg.cn.push({
2232                     tag : 'div',
2233                     cls : 'card-img-top d-none' 
2234                 });
2235         }
2236             
2237         var body = {
2238             tag : 'div',
2239             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2240             cn : []
2241         };
2242         var obody = body;
2243         if (this.collapsable || this.rotateable) {
2244             obody = {
2245                 tag: 'div',
2246                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2247                 cn : [  body ]
2248             };
2249         }
2250         
2251         cfg.cn.push(obody);
2252         
2253         if (this.title.length) {
2254             body.cn.push({
2255                 tag : 'div',
2256                 cls : 'card-title',
2257                 src: this.title // escape?
2258             });
2259         }  
2260         
2261         if (this.subtitle.length) {
2262             body.cn.push({
2263                 tag : 'div',
2264                 cls : 'card-title',
2265                 src: this.subtitle // escape?
2266             });
2267         }
2268         
2269         body.cn.push({
2270             tag : 'div',
2271             cls : 'roo-card-body-ctr'
2272         });
2273         
2274         if (this.html.length) {
2275             body.cn.push({
2276                 tag: 'div',
2277                 html : this.html
2278             });
2279         }
2280         // fixme ? handle objects?
2281         
2282         if (this.footer.length) {
2283            
2284             cfg.cn.push({
2285                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2286                 html : this.footer
2287             });
2288             
2289         } else {
2290             cfg.cn.push({cls : 'card-footer d-none'});
2291         }
2292         
2293         // footer...
2294         
2295         return cfg;
2296     },
2297     
2298     
2299     getCardHeader : function()
2300     {
2301         var  ret = this.el.select('.card-header',true).first();
2302         if (ret.hasClass('d-none')) {
2303             ret.removeClass('d-none');
2304         }
2305         
2306         return ret;
2307     },
2308     getCardFooter : function()
2309     {
2310         var  ret = this.el.select('.card-footer',true).first();
2311         if (ret.hasClass('d-none')) {
2312             ret.removeClass('d-none');
2313         }
2314         
2315         return ret;
2316     },
2317     getCardImageTop : function()
2318     {
2319         var  ret = this.header_imageEl;
2320         if (ret.hasClass('d-none')) {
2321             ret.removeClass('d-none');
2322         }
2323             
2324         return ret;
2325     },
2326     
2327     getChildContainer : function()
2328     {
2329         
2330         if(!this.el){
2331             return false;
2332         }
2333         return this.el.select('.roo-card-body-ctr',true).first();    
2334     },
2335     
2336     initEvents: function() 
2337     {
2338         this.bodyEl = this.el.select('.card-body',true).first(); 
2339         this.containerEl = this.getChildContainer();
2340         if(this.dragable){
2341             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342                     containerScroll: true,
2343                     ddGroup: this.drag_group || 'default_card_drag_group'
2344             });
2345             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2346         }
2347         if (this.dropable) {
2348             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349                 containerScroll: true,
2350                 ddGroup: this.drop_group || 'default_card_drag_group'
2351             });
2352             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2357         }
2358         
2359         if (this.collapsable) {
2360             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2361         }
2362         if (this.rotateable) {
2363             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2364         }
2365         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2366          
2367         this.footerEl = this.el.select('.card-footer',true).first();
2368         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370         this.headerEl = this.el.select('.card-header',true).first();
2371         
2372         if (this.rotated) {
2373             this.el.addClass('roo-card-rotated');
2374             this.fireEvent('rotate', this, true);
2375         }
2376         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2377         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2378         
2379     },
2380     getDragData : function(e)
2381     {
2382         var target = this.getEl();
2383         if (target) {
2384             //this.handleSelection(e);
2385             
2386             var dragData = {
2387                 source: this,
2388                 copy: false,
2389                 nodes: this.getEl(),
2390                 records: []
2391             };
2392             
2393             
2394             dragData.ddel = target.dom ;    // the div element
2395             Roo.log(target.getWidth( ));
2396             dragData.ddel.style.width = target.getWidth() + 'px';
2397             
2398             return dragData;
2399         }
2400         return false;
2401     },
2402     /**
2403     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2404     *    whole Element becomes the target, and this causes the drop gesture to append.
2405     *
2406     *    Returns an object:
2407     *     {
2408            
2409            position : 'below' or 'above'
2410            card  : relateive to card OBJECT (or true for no cards listed)
2411            items_n : relative to nth item in list
2412            card_n : relative to  nth card in list
2413     }
2414     *
2415     *    
2416     */
2417     getTargetFromEvent : function(e, dragged_card_el)
2418     {
2419         var target = e.getTarget();
2420         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421             target = target.parentNode;
2422         }
2423         
2424         var ret = {
2425             position: '',
2426             cards : [],
2427             card_n : -1,
2428             items_n : -1,
2429             card : false 
2430         };
2431         
2432         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433         // see if target is one of the 'cards'...
2434         
2435         
2436         //Roo.log(this.items.length);
2437         var pos = false;
2438         
2439         var last_card_n = 0;
2440         var cards_len  = 0;
2441         for (var i = 0;i< this.items.length;i++) {
2442             
2443             if (!this.items[i].el.hasClass('card')) {
2444                  continue;
2445             }
2446             pos = this.getDropPoint(e, this.items[i].el.dom);
2447             
2448             cards_len = ret.cards.length;
2449             //Roo.log(this.items[i].el.dom.id);
2450             ret.cards.push(this.items[i]);
2451             last_card_n  = i;
2452             if (ret.card_n < 0 && pos == 'above') {
2453                 ret.position = cards_len > 0 ? 'below' : pos;
2454                 ret.items_n = i > 0 ? i - 1 : 0;
2455                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2456                 ret.card = ret.cards[ret.card_n];
2457             }
2458         }
2459         if (!ret.cards.length) {
2460             ret.card = true;
2461             ret.position = 'below';
2462             ret.items_n;
2463             return ret;
2464         }
2465         // could not find a card.. stick it at the end..
2466         if (ret.card_n < 0) {
2467             ret.card_n = last_card_n;
2468             ret.card = ret.cards[last_card_n];
2469             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470             ret.position = 'below';
2471         }
2472         
2473         if (this.items[ret.items_n].el == dragged_card_el) {
2474             return false;
2475         }
2476         
2477         if (ret.position == 'below') {
2478             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2479             
2480             if (card_after  && card_after.el == dragged_card_el) {
2481                 return false;
2482             }
2483             return ret;
2484         }
2485         
2486         // its's after ..
2487         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2488         
2489         if (card_before  && card_before.el == dragged_card_el) {
2490             return false;
2491         }
2492         
2493         return ret;
2494     },
2495     
2496     onNodeEnter : function(n, dd, e, data){
2497         return false;
2498     },
2499     onNodeOver : function(n, dd, e, data)
2500     {
2501        
2502         var target_info = this.getTargetFromEvent(e,data.source.el);
2503         if (target_info === false) {
2504             this.dropPlaceHolder('hide');
2505             return false;
2506         }
2507         Roo.log(['getTargetFromEvent', target_info ]);
2508         
2509         
2510         if (this.fireEvent('cardover', this, [ data ]) === false) {
2511             return false;
2512         }
2513         
2514         this.dropPlaceHolder('show', target_info,data);
2515         
2516         return false; 
2517     },
2518     onNodeOut : function(n, dd, e, data){
2519         this.dropPlaceHolder('hide');
2520      
2521     },
2522     onNodeDrop : function(n, dd, e, data)
2523     {
2524         
2525         // call drop - return false if
2526         
2527         // this could actually fail - if the Network drops..
2528         // we will ignore this at present..- client should probably reload
2529         // the whole set of cards if stuff like that fails.
2530         
2531         
2532         var info = this.getTargetFromEvent(e,data.source.el);
2533         if (info === false) {
2534             return false;
2535         }
2536         this.dropPlaceHolder('hide');
2537   
2538           
2539     
2540         this.acceptCard(data.source, info.position, info.card, info.items_n);
2541         return true;
2542          
2543     },
2544     firstChildCard : function()
2545     {
2546         for (var i = 0;i< this.items.length;i++) {
2547             
2548             if (!this.items[i].el.hasClass('card')) {
2549                  continue;
2550             }
2551             return this.items[i];
2552         }
2553         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2554     },
2555     /**
2556      * accept card
2557      *
2558      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2559      */
2560     acceptCard : function(move_card,  position, next_to_card )
2561     {
2562         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2563             return false;
2564         }
2565         
2566         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2567         
2568         move_card.parent().removeCard(move_card);
2569         
2570         
2571         var dom = move_card.el.dom;
2572         dom.style.width = ''; // clear with - which is set by drag.
2573         
2574         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575             var cardel = next_to_card.el.dom;
2576             
2577             if (position == 'above' ) {
2578                 cardel.parentNode.insertBefore(dom, cardel);
2579             } else if (cardel.nextSibling) {
2580                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2581             } else {
2582                 cardel.parentNode.append(dom);
2583             }
2584         } else {
2585             // card container???
2586             this.containerEl.dom.append(dom);
2587         }
2588         
2589         //FIXME HANDLE card = true 
2590         
2591         // add this to the correct place in items.
2592         
2593         // remove Card from items.
2594         
2595        
2596         if (this.items.length) {
2597             var nitems = [];
2598             //Roo.log([info.items_n, info.position, this.items.length]);
2599             for (var i =0; i < this.items.length; i++) {
2600                 if (i == to_items_n && position == 'above') {
2601                     nitems.push(move_card);
2602                 }
2603                 nitems.push(this.items[i]);
2604                 if (i == to_items_n && position == 'below') {
2605                     nitems.push(move_card);
2606                 }
2607             }
2608             this.items = nitems;
2609             Roo.log(this.items);
2610         } else {
2611             this.items.push(move_card);
2612         }
2613         
2614         move_card.parentId = this.id;
2615         
2616         return true;
2617         
2618         
2619     },
2620     removeCard : function(c)
2621     {
2622         this.items = this.items.filter(function(e) { return e != c });
2623  
2624         var dom = c.el.dom;
2625         dom.parentNode.removeChild(dom);
2626         dom.style.width = ''; // clear with - which is set by drag.
2627         c.parentId = false;
2628         
2629     },
2630     
2631     /**    Decide whether to drop above or below a View node. */
2632     getDropPoint : function(e, n, dd)
2633     {
2634         if (dd) {
2635              return false;
2636         }
2637         if (n == this.containerEl.dom) {
2638             return "above";
2639         }
2640         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641         var c = t + (b - t) / 2;
2642         var y = Roo.lib.Event.getPageY(e);
2643         if(y <= c) {
2644             return "above";
2645         }else{
2646             return "below";
2647         }
2648     },
2649     onToggleCollapse : function(e)
2650         {
2651         if (this.collapsed) {
2652             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653             this.collapsableEl.addClass('show');
2654             this.collapsed = false;
2655             return;
2656         }
2657         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658         this.collapsableEl.removeClass('show');
2659         this.collapsed = true;
2660         
2661     
2662     },
2663     
2664     onToggleRotate : function(e)
2665     {
2666         this.collapsableEl.removeClass('show');
2667         this.footerEl.removeClass('d-none');
2668         this.el.removeClass('roo-card-rotated');
2669         this.el.removeClass('d-none');
2670         if (this.rotated) {
2671             
2672             this.collapsableEl.addClass('show');
2673             this.rotated = false;
2674             this.fireEvent('rotate', this, this.rotated);
2675             return;
2676         }
2677         this.el.addClass('roo-card-rotated');
2678         this.footerEl.addClass('d-none');
2679         this.el.select('.roo-collapsable').removeClass('show');
2680         
2681         this.rotated = true;
2682         this.fireEvent('rotate', this, this.rotated);
2683     
2684     },
2685     
2686     dropPlaceHolder: function (action, info, data)
2687     {
2688         if (this.dropEl === false) {
2689             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2690             cls : 'd-none'
2691             },true);
2692         }
2693         this.dropEl.removeClass(['d-none', 'd-block']);        
2694         if (action == 'hide') {
2695             
2696             this.dropEl.addClass('d-none');
2697             return;
2698         }
2699         // FIXME - info.card == true!!!
2700         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2701         
2702         if (info.card !== true) {
2703             var cardel = info.card.el.dom;
2704             
2705             if (info.position == 'above') {
2706                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707             } else if (cardel.nextSibling) {
2708                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2709             } else {
2710                 cardel.parentNode.append(this.dropEl.dom);
2711             }
2712         } else {
2713             // card container???
2714             this.containerEl.dom.append(this.dropEl.dom);
2715         }
2716         
2717         this.dropEl.addClass('d-block roo-card-dropzone');
2718         
2719         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2720         
2721         
2722     
2723     
2724     
2725     },
2726     setHeaderText: function(html)
2727     {
2728         this.header = html;
2729         if (this.headerContainerEl) {
2730             this.headerContainerEl.dom.innerHTML = html;
2731         }
2732     },
2733     onHeaderImageLoad : function(ev, he)
2734     {
2735         if (!this.header_image_fit_square) {
2736             return;
2737         }
2738         
2739         var hw = he.naturalHeight / he.naturalWidth;
2740         // wide image = < 0
2741         // tall image = > 1
2742         //var w = he.dom.naturalWidth;
2743         var ww = he.width;
2744         he.style.left =  0;
2745         he.style.position =  'relative';
2746         if (hw > 1) {
2747             var nw = (ww * (1/hw));
2748             Roo.get(he).setSize( ww * (1/hw),  ww);
2749             he.style.left =  ((ww - nw)/ 2) + 'px';
2750             he.style.position =  'relative';
2751         }
2752
2753     }
2754
2755     
2756 });
2757
2758 /*
2759  * - LGPL
2760  *
2761  * Card header - holder for the card header elements.
2762  * 
2763  */
2764
2765 /**
2766  * @class Roo.bootstrap.CardHeader
2767  * @extends Roo.bootstrap.Element
2768  * Bootstrap CardHeader class
2769  * @constructor
2770  * Create a new Card Header - that you can embed children into
2771  * @param {Object} config The config object
2772  */
2773
2774 Roo.bootstrap.CardHeader = function(config){
2775     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2776 };
2777
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2779     
2780     
2781     container_method : 'getCardHeader' 
2782     
2783      
2784     
2785     
2786    
2787 });
2788
2789  
2790
2791  /*
2792  * - LGPL
2793  *
2794  * Card footer - holder for the card footer elements.
2795  * 
2796  */
2797
2798 /**
2799  * @class Roo.bootstrap.CardFooter
2800  * @extends Roo.bootstrap.Element
2801  * Bootstrap CardFooter class
2802  * @constructor
2803  * Create a new Card Footer - that you can embed children into
2804  * @param {Object} config The config object
2805  */
2806
2807 Roo.bootstrap.CardFooter = function(config){
2808     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2809 };
2810
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2812     
2813     
2814     container_method : 'getCardFooter' 
2815     
2816      
2817     
2818     
2819    
2820 });
2821
2822  
2823
2824  /*
2825  * - LGPL
2826  *
2827  * Card header - holder for the card header elements.
2828  * 
2829  */
2830
2831 /**
2832  * @class Roo.bootstrap.CardImageTop
2833  * @extends Roo.bootstrap.Element
2834  * Bootstrap CardImageTop class
2835  * @constructor
2836  * Create a new Card Image Top container
2837  * @param {Object} config The config object
2838  */
2839
2840 Roo.bootstrap.CardImageTop = function(config){
2841     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2842 };
2843
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2845     
2846    
2847     container_method : 'getCardImageTop' 
2848     
2849      
2850     
2851    
2852 });
2853
2854  
2855
2856  
2857 /*
2858 * Licence: LGPL
2859 */
2860
2861 /**
2862  * @class Roo.bootstrap.ButtonUploader
2863  * @extends Roo.bootstrap.Button
2864  * Bootstrap Button Uploader class - it's a button which when you add files to it
2865  *
2866  * 
2867  * @cfg {Number} errorTimeout default 3000
2868  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2869  * @cfg {Array}  html The button text.
2870  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2871  *
2872  * @constructor
2873  * Create a new CardUploader
2874  * @param {Object} config The config object
2875  */
2876
2877 Roo.bootstrap.ButtonUploader = function(config){
2878     
2879  
2880     
2881     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2882     
2883      
2884      this.addEvents({
2885          // raw events
2886         /**
2887          * @event beforeselect
2888          * When button is pressed, before show upload files dialog is shown
2889          * @param {Roo.bootstrap.UploaderButton} this
2890          *
2891          */
2892         'beforeselect' : true,
2893          /**
2894          * @event fired when files have been selected, 
2895          * When a the download link is clicked
2896          * @param {Roo.bootstrap.UploaderButton} this
2897          * @param {Array} Array of files that have been uploaded
2898          */
2899         'uploaded' : true
2900         
2901     });
2902 };
2903  
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2905     
2906      
2907     errorTimeout : 3000,
2908      
2909     images : false,
2910    
2911     fileCollection : false,
2912     allowBlank : true,
2913     
2914     multiple : true,
2915     
2916     getAutoCreate : function()
2917     {
2918         var im = {
2919             tag: 'input',
2920             type : 'file',
2921             cls : 'd-none  roo-card-upload-selector' 
2922           
2923         };
2924         if (this.multiple) {
2925             im.multiple = 'multiple';
2926         }
2927         
2928         return  {
2929             cls :'div' ,
2930             cn : [
2931                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2932                 im
2933
2934             ]
2935         };
2936            
2937          
2938     },
2939      
2940    
2941     initEvents : function()
2942     {
2943         
2944         Roo.bootstrap.Button.prototype.initEvents.call(this);
2945         
2946         
2947         
2948         
2949         
2950         this.urlAPI = (window.createObjectURL && window) || 
2951                                 (window.URL && URL.revokeObjectURL && URL) || 
2952                                 (window.webkitURL && webkitURL);
2953                         
2954          
2955          
2956          
2957         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2958         
2959         this.selectorEl.on('change', this.onFileSelected, this);
2960          
2961          
2962        
2963     },
2964     
2965    
2966     onClick : function(e)
2967     {
2968         e.preventDefault();
2969         
2970         if ( this.fireEvent('beforeselect', this) === false) {
2971             return;
2972         }
2973          
2974         this.selectorEl.dom.click();
2975          
2976     },
2977     
2978     onFileSelected : function(e)
2979     {
2980         e.preventDefault();
2981         
2982         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2983             return;
2984         }
2985         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986         this.selectorEl.dom.value  = '';// hopefully reset..
2987         
2988         this.fireEvent('uploaded', this,  files );
2989         
2990     },
2991     
2992        
2993    
2994     
2995     /**
2996      * addCard - add an Attachment to the uploader
2997      * @param data - the data about the image to upload
2998      *
2999      * {
3000           id : 123
3001           title : "Title of file",
3002           is_uploaded : false,
3003           src : "http://.....",
3004           srcfile : { the File upload object },
3005           mimetype : file.type,
3006           preview : false,
3007           is_deleted : 0
3008           .. any other data...
3009         }
3010      *
3011      * 
3012     */
3013      
3014     reset: function()
3015     {
3016          
3017          this.selectorEl
3018     } 
3019     
3020     
3021     
3022     
3023 });
3024  /*
3025  * - LGPL
3026  *
3027  * image
3028  * 
3029  */
3030
3031
3032 /**
3033  * @class Roo.bootstrap.Img
3034  * @extends Roo.bootstrap.Component
3035  * Bootstrap Img class
3036  * @cfg {Boolean} imgResponsive false | true
3037  * @cfg {String} border rounded | circle | thumbnail
3038  * @cfg {String} src image source
3039  * @cfg {String} alt image alternative text
3040  * @cfg {String} href a tag href
3041  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042  * @cfg {String} xsUrl xs image source
3043  * @cfg {String} smUrl sm image source
3044  * @cfg {String} mdUrl md image source
3045  * @cfg {String} lgUrl lg image source
3046  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3047  * 
3048  * @constructor
3049  * Create a new Input
3050  * @param {Object} config The config object
3051  */
3052
3053 Roo.bootstrap.Img = function(config){
3054     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3055     
3056     this.addEvents({
3057         // img events
3058         /**
3059          * @event click
3060          * The img click event for the img.
3061          * @param {Roo.EventObject} e
3062          */
3063         "click" : true,
3064         /**
3065          * @event load
3066          * The when any image loads
3067          * @param {Roo.EventObject} e
3068          */
3069         "load" : true
3070     });
3071 };
3072
3073 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3074     
3075     imgResponsive: true,
3076     border: '',
3077     src: 'about:blank',
3078     href: false,
3079     target: false,
3080     xsUrl: '',
3081     smUrl: '',
3082     mdUrl: '',
3083     lgUrl: '',
3084     backgroundContain : false,
3085
3086     getAutoCreate : function()
3087     {   
3088         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3089             return this.createSingleImg();
3090         }
3091         
3092         var cfg = {
3093             tag: 'div',
3094             cls: 'roo-image-responsive-group',
3095             cn: []
3096         };
3097         var _this = this;
3098         
3099         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3100             
3101             if(!_this[size + 'Url']){
3102                 return;
3103             }
3104             
3105             var img = {
3106                 tag: 'img',
3107                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3108                 html: _this.html || cfg.html,
3109                 src: _this[size + 'Url']
3110             };
3111             
3112             img.cls += ' roo-image-responsive-' + size;
3113             
3114             var s = ['xs', 'sm', 'md', 'lg'];
3115             
3116             s.splice(s.indexOf(size), 1);
3117             
3118             Roo.each(s, function(ss){
3119                 img.cls += ' hidden-' + ss;
3120             });
3121             
3122             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3123                 cfg.cls += ' img-' + _this.border;
3124             }
3125             
3126             if(_this.alt){
3127                 cfg.alt = _this.alt;
3128             }
3129             
3130             if(_this.href){
3131                 var a = {
3132                     tag: 'a',
3133                     href: _this.href,
3134                     cn: [
3135                         img
3136                     ]
3137                 };
3138
3139                 if(this.target){
3140                     a.target = _this.target;
3141                 }
3142             }
3143             
3144             cfg.cn.push((_this.href) ? a : img);
3145             
3146         });
3147         
3148         return cfg;
3149     },
3150     
3151     createSingleImg : function()
3152     {
3153         var cfg = {
3154             tag: 'img',
3155             cls: (this.imgResponsive) ? 'img-responsive' : '',
3156             html : null,
3157             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3158         };
3159         
3160         if (this.backgroundContain) {
3161             cfg.cls += ' background-contain';
3162         }
3163         
3164         cfg.html = this.html || cfg.html;
3165         
3166         if (this.backgroundContain) {
3167             cfg.style="background-image: url(" + this.src + ')';
3168         } else {
3169             cfg.src = this.src || cfg.src;
3170         }
3171         
3172         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3173             cfg.cls += ' img-' + this.border;
3174         }
3175         
3176         if(this.alt){
3177             cfg.alt = this.alt;
3178         }
3179         
3180         if(this.href){
3181             var a = {
3182                 tag: 'a',
3183                 href: this.href,
3184                 cn: [
3185                     cfg
3186                 ]
3187             };
3188             
3189             if(this.target){
3190                 a.target = this.target;
3191             }
3192             
3193         }
3194         
3195         return (this.href) ? a : cfg;
3196     },
3197     
3198     initEvents: function() 
3199     {
3200         if(!this.href){
3201             this.el.on('click', this.onClick, this);
3202         }
3203         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3204             this.el.on('load', this.onImageLoad, this);
3205         } else {
3206             // not sure if this works.. not tested
3207             this.el.select('img', true).on('load', this.onImageLoad, this);
3208         }
3209         
3210     },
3211     
3212     onClick : function(e)
3213     {
3214         Roo.log('img onclick');
3215         this.fireEvent('click', this, e);
3216     },
3217     onImageLoad: function(e)
3218     {
3219         Roo.log('img load');
3220         this.fireEvent('load', this, e);
3221     },
3222     
3223     /**
3224      * Sets the url of the image - used to update it
3225      * @param {String} url the url of the image
3226      */
3227     
3228     setSrc : function(url)
3229     {
3230         this.src =  url;
3231         
3232         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233             if (this.backgroundContain) {
3234                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3235             } else {
3236                 this.el.dom.src =  url;
3237             }
3238             return;
3239         }
3240         
3241         this.el.select('img', true).first().dom.src =  url;
3242     }
3243     
3244     
3245    
3246 });
3247
3248  /*
3249  * - LGPL
3250  *
3251  * image
3252  * 
3253  */
3254
3255
3256 /**
3257  * @class Roo.bootstrap.Link
3258  * @extends Roo.bootstrap.Component
3259  * Bootstrap Link Class
3260  * @cfg {String} alt image alternative text
3261  * @cfg {String} href a tag href
3262  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3263  * @cfg {String} html the content of the link.
3264  * @cfg {String} anchor name for the anchor link
3265  * @cfg {String} fa - favicon
3266
3267  * @cfg {Boolean} preventDefault (true | false) default false
3268
3269  * 
3270  * @constructor
3271  * Create a new Input
3272  * @param {Object} config The config object
3273  */
3274
3275 Roo.bootstrap.Link = function(config){
3276     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3277     
3278     this.addEvents({
3279         // img events
3280         /**
3281          * @event click
3282          * The img click event for the img.
3283          * @param {Roo.EventObject} e
3284          */
3285         "click" : true
3286     });
3287 };
3288
3289 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3290     
3291     href: false,
3292     target: false,
3293     preventDefault: false,
3294     anchor : false,
3295     alt : false,
3296     fa: false,
3297
3298
3299     getAutoCreate : function()
3300     {
3301         var html = this.html || '';
3302         
3303         if (this.fa !== false) {
3304             html = '<i class="fa fa-' + this.fa + '"></i>';
3305         }
3306         var cfg = {
3307             tag: 'a'
3308         };
3309         // anchor's do not require html/href...
3310         if (this.anchor === false) {
3311             cfg.html = html;
3312             cfg.href = this.href || '#';
3313         } else {
3314             cfg.name = this.anchor;
3315             if (this.html !== false || this.fa !== false) {
3316                 cfg.html = html;
3317             }
3318             if (this.href !== false) {
3319                 cfg.href = this.href;
3320             }
3321         }
3322         
3323         if(this.alt !== false){
3324             cfg.alt = this.alt;
3325         }
3326         
3327         
3328         if(this.target !== false) {
3329             cfg.target = this.target;
3330         }
3331         
3332         return cfg;
3333     },
3334     
3335     initEvents: function() {
3336         
3337         if(!this.href || this.preventDefault){
3338             this.el.on('click', this.onClick, this);
3339         }
3340     },
3341     
3342     onClick : function(e)
3343     {
3344         if(this.preventDefault){
3345             e.preventDefault();
3346         }
3347         //Roo.log('img onclick');
3348         this.fireEvent('click', this, e);
3349     }
3350    
3351 });
3352
3353  /*
3354  * - LGPL
3355  *
3356  * header
3357  * 
3358  */
3359
3360 /**
3361  * @class Roo.bootstrap.Header
3362  * @extends Roo.bootstrap.Component
3363  * Bootstrap Header class
3364  * @cfg {String} html content of header
3365  * @cfg {Number} level (1|2|3|4|5|6) default 1
3366  * 
3367  * @constructor
3368  * Create a new Header
3369  * @param {Object} config The config object
3370  */
3371
3372
3373 Roo.bootstrap.Header  = function(config){
3374     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3375 };
3376
3377 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3378     
3379     //href : false,
3380     html : false,
3381     level : 1,
3382     
3383     
3384     
3385     getAutoCreate : function(){
3386         
3387         
3388         
3389         var cfg = {
3390             tag: 'h' + (1 *this.level),
3391             html: this.html || ''
3392         } ;
3393         
3394         return cfg;
3395     }
3396    
3397 });
3398
3399  
3400
3401  /*
3402  * Based on:
3403  * Ext JS Library 1.1.1
3404  * Copyright(c) 2006-2007, Ext JS, LLC.
3405  *
3406  * Originally Released Under LGPL - original licence link has changed is not relivant.
3407  *
3408  * Fork - LGPL
3409  * <script type="text/javascript">
3410  */
3411  
3412 /**
3413  * @class Roo.bootstrap.MenuMgr
3414  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3415  * @singleton
3416  */
3417 Roo.bootstrap.MenuMgr = function(){
3418    var menus, active, groups = {}, attached = false, lastShow = new Date();
3419
3420    // private - called when first menu is created
3421    function init(){
3422        menus = {};
3423        active = new Roo.util.MixedCollection();
3424        Roo.get(document).addKeyListener(27, function(){
3425            if(active.length > 0){
3426                hideAll();
3427            }
3428        });
3429    }
3430
3431    // private
3432    function hideAll(){
3433        if(active && active.length > 0){
3434            var c = active.clone();
3435            c.each(function(m){
3436                m.hide();
3437            });
3438        }
3439    }
3440
3441    // private
3442    function onHide(m){
3443        active.remove(m);
3444        if(active.length < 1){
3445            Roo.get(document).un("mouseup", onMouseDown);
3446             
3447            attached = false;
3448        }
3449    }
3450
3451    // private
3452    function onShow(m){
3453        var last = active.last();
3454        lastShow = new Date();
3455        active.add(m);
3456        if(!attached){
3457           Roo.get(document).on("mouseup", onMouseDown);
3458            
3459            attached = true;
3460        }
3461        if(m.parentMenu){
3462           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3463           m.parentMenu.activeChild = m;
3464        }else if(last && last.isVisible()){
3465           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3466        }
3467    }
3468
3469    // private
3470    function onBeforeHide(m){
3471        if(m.activeChild){
3472            m.activeChild.hide();
3473        }
3474        if(m.autoHideTimer){
3475            clearTimeout(m.autoHideTimer);
3476            delete m.autoHideTimer;
3477        }
3478    }
3479
3480    // private
3481    function onBeforeShow(m){
3482        var pm = m.parentMenu;
3483        if(!pm && !m.allowOtherMenus){
3484            hideAll();
3485        }else if(pm && pm.activeChild && active != m){
3486            pm.activeChild.hide();
3487        }
3488    }
3489
3490    // private this should really trigger on mouseup..
3491    function onMouseDown(e){
3492         Roo.log("on Mouse Up");
3493         
3494         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3495             Roo.log("MenuManager hideAll");
3496             hideAll();
3497             e.stopEvent();
3498         }
3499         
3500         
3501    }
3502
3503    // private
3504    function onBeforeCheck(mi, state){
3505        if(state){
3506            var g = groups[mi.group];
3507            for(var i = 0, l = g.length; i < l; i++){
3508                if(g[i] != mi){
3509                    g[i].setChecked(false);
3510                }
3511            }
3512        }
3513    }
3514
3515    return {
3516
3517        /**
3518         * Hides all menus that are currently visible
3519         */
3520        hideAll : function(){
3521             hideAll();  
3522        },
3523
3524        // private
3525        register : function(menu){
3526            if(!menus){
3527                init();
3528            }
3529            menus[menu.id] = menu;
3530            menu.on("beforehide", onBeforeHide);
3531            menu.on("hide", onHide);
3532            menu.on("beforeshow", onBeforeShow);
3533            menu.on("show", onShow);
3534            var g = menu.group;
3535            if(g && menu.events["checkchange"]){
3536                if(!groups[g]){
3537                    groups[g] = [];
3538                }
3539                groups[g].push(menu);
3540                menu.on("checkchange", onCheck);
3541            }
3542        },
3543
3544         /**
3545          * Returns a {@link Roo.menu.Menu} object
3546          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3547          * be used to generate and return a new Menu instance.
3548          */
3549        get : function(menu){
3550            if(typeof menu == "string"){ // menu id
3551                return menus[menu];
3552            }else if(menu.events){  // menu instance
3553                return menu;
3554            }
3555            /*else if(typeof menu.length == 'number'){ // array of menu items?
3556                return new Roo.bootstrap.Menu({items:menu});
3557            }else{ // otherwise, must be a config
3558                return new Roo.bootstrap.Menu(menu);
3559            }
3560            */
3561            return false;
3562        },
3563
3564        // private
3565        unregister : function(menu){
3566            delete menus[menu.id];
3567            menu.un("beforehide", onBeforeHide);
3568            menu.un("hide", onHide);
3569            menu.un("beforeshow", onBeforeShow);
3570            menu.un("show", onShow);
3571            var g = menu.group;
3572            if(g && menu.events["checkchange"]){
3573                groups[g].remove(menu);
3574                menu.un("checkchange", onCheck);
3575            }
3576        },
3577
3578        // private
3579        registerCheckable : function(menuItem){
3580            var g = menuItem.group;
3581            if(g){
3582                if(!groups[g]){
3583                    groups[g] = [];
3584                }
3585                groups[g].push(menuItem);
3586                menuItem.on("beforecheckchange", onBeforeCheck);
3587            }
3588        },
3589
3590        // private
3591        unregisterCheckable : function(menuItem){
3592            var g = menuItem.group;
3593            if(g){
3594                groups[g].remove(menuItem);
3595                menuItem.un("beforecheckchange", onBeforeCheck);
3596            }
3597        }
3598    };
3599 }();/*
3600  * - LGPL
3601  *
3602  * menu
3603  * 
3604  */
3605
3606 /**
3607  * @class Roo.bootstrap.Menu
3608  * @extends Roo.bootstrap.Component
3609  * Bootstrap Menu class - container for MenuItems
3610  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3611  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3612  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3613  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3614   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3615   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3616  
3617  * @constructor
3618  * Create a new Menu
3619  * @param {Object} config The config object
3620  */
3621
3622
3623 Roo.bootstrap.Menu = function(config){
3624     
3625     if (config.type == 'treeview') {
3626         // normally menu's are drawn attached to the document to handle layering etc..
3627         // however treeview (used by the docs menu is drawn into the parent element)
3628         this.container_method = 'getChildContainer'; 
3629     }
3630     
3631     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3632     if (this.registerMenu && this.type != 'treeview')  {
3633         Roo.bootstrap.MenuMgr.register(this);
3634     }
3635     
3636     
3637     this.addEvents({
3638         /**
3639          * @event beforeshow
3640          * Fires before this menu is displayed (return false to block)
3641          * @param {Roo.menu.Menu} this
3642          */
3643         beforeshow : true,
3644         /**
3645          * @event beforehide
3646          * Fires before this menu is hidden (return false to block)
3647          * @param {Roo.menu.Menu} this
3648          */
3649         beforehide : true,
3650         /**
3651          * @event show
3652          * Fires after this menu is displayed
3653          * @param {Roo.menu.Menu} this
3654          */
3655         show : true,
3656         /**
3657          * @event hide
3658          * Fires after this menu is hidden
3659          * @param {Roo.menu.Menu} this
3660          */
3661         hide : true,
3662         /**
3663          * @event click
3664          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3665          * @param {Roo.menu.Menu} this
3666          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3667          * @param {Roo.EventObject} e
3668          */
3669         click : true,
3670         /**
3671          * @event mouseover
3672          * Fires when the mouse is hovering over this menu
3673          * @param {Roo.menu.Menu} this
3674          * @param {Roo.EventObject} e
3675          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3676          */
3677         mouseover : true,
3678         /**
3679          * @event mouseout
3680          * Fires when the mouse exits this menu
3681          * @param {Roo.menu.Menu} this
3682          * @param {Roo.EventObject} e
3683          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3684          */
3685         mouseout : true,
3686         /**
3687          * @event itemclick
3688          * Fires when a menu item contained in this menu is clicked
3689          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3690          * @param {Roo.EventObject} e
3691          */
3692         itemclick: true
3693     });
3694     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3695 };
3696
3697 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3698     
3699    /// html : false,
3700    
3701     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3702     type: false,
3703     /**
3704      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3705      */
3706     registerMenu : true,
3707     
3708     menuItems :false, // stores the menu items..
3709     
3710     hidden:true,
3711         
3712     parentMenu : false,
3713     
3714     stopEvent : true,
3715     
3716     isLink : false,
3717     
3718     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3719     
3720     hideTrigger : false,
3721     
3722     align : 'tl-bl?',
3723     
3724     
3725     getChildContainer : function() {
3726         return this.el;  
3727     },
3728     
3729     getAutoCreate : function(){
3730          
3731         //if (['right'].indexOf(this.align)!==-1) {
3732         //    cfg.cn[1].cls += ' pull-right'
3733         //}
3734          
3735         var cfg = {
3736             tag : 'ul',
3737             cls : 'dropdown-menu shadow' ,
3738             style : 'z-index:1000'
3739             
3740         };
3741         
3742         if (this.type === 'submenu') {
3743             cfg.cls = 'submenu active';
3744         }
3745         if (this.type === 'treeview') {
3746             cfg.cls = 'treeview-menu';
3747         }
3748         
3749         return cfg;
3750     },
3751     initEvents : function() {
3752         
3753        // Roo.log("ADD event");
3754        // Roo.log(this.triggerEl.dom);
3755         if (this.triggerEl) {
3756             
3757             this.triggerEl.on('click', this.onTriggerClick, this);
3758             
3759             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3760             
3761             if (!this.hideTrigger) {
3762                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3763                     // dropdown toggle on the 'a' in BS4?
3764                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3765                 } else {
3766                     this.triggerEl.addClass('dropdown-toggle');
3767                 }
3768             }
3769         }
3770         
3771         if (Roo.isTouch) {
3772             this.el.on('touchstart'  , this.onTouch, this);
3773         }
3774         this.el.on('click' , this.onClick, this);
3775
3776         this.el.on("mouseover", this.onMouseOver, this);
3777         this.el.on("mouseout", this.onMouseOut, this);
3778         
3779     },
3780     
3781     findTargetItem : function(e)
3782     {
3783         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3784         if(!t){
3785             return false;
3786         }
3787         //Roo.log(t);         Roo.log(t.id);
3788         if(t && t.id){
3789             //Roo.log(this.menuitems);
3790             return this.menuitems.get(t.id);
3791             
3792             //return this.items.get(t.menuItemId);
3793         }
3794         
3795         return false;
3796     },
3797     
3798     onTouch : function(e) 
3799     {
3800         Roo.log("menu.onTouch");
3801         //e.stopEvent(); this make the user popdown broken
3802         this.onClick(e);
3803     },
3804     
3805     onClick : function(e)
3806     {
3807         Roo.log("menu.onClick");
3808         
3809         var t = this.findTargetItem(e);
3810         if(!t || t.isContainer){
3811             return;
3812         }
3813         Roo.log(e);
3814         /*
3815         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3816             if(t == this.activeItem && t.shouldDeactivate(e)){
3817                 this.activeItem.deactivate();
3818                 delete this.activeItem;
3819                 return;
3820             }
3821             if(t.canActivate){
3822                 this.setActiveItem(t, true);
3823             }
3824             return;
3825             
3826             
3827         }
3828         */
3829        
3830         Roo.log('pass click event');
3831         
3832         t.onClick(e);
3833         
3834         this.fireEvent("click", this, t, e);
3835         
3836         var _this = this;
3837         
3838         if(!t.href.length || t.href == '#'){
3839             (function() { _this.hide(); }).defer(100);
3840         }
3841         
3842     },
3843     
3844     onMouseOver : function(e){
3845         var t  = this.findTargetItem(e);
3846         //Roo.log(t);
3847         //if(t){
3848         //    if(t.canActivate && !t.disabled){
3849         //        this.setActiveItem(t, true);
3850         //    }
3851         //}
3852         
3853         this.fireEvent("mouseover", this, e, t);
3854     },
3855     isVisible : function(){
3856         return !this.hidden;
3857     },
3858     onMouseOut : function(e){
3859         var t  = this.findTargetItem(e);
3860         
3861         //if(t ){
3862         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3863         //        this.activeItem.deactivate();
3864         //        delete this.activeItem;
3865         //    }
3866         //}
3867         this.fireEvent("mouseout", this, e, t);
3868     },
3869     
3870     
3871     /**
3872      * Displays this menu relative to another element
3873      * @param {String/HTMLElement/Roo.Element} element The element to align to
3874      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3875      * the element (defaults to this.defaultAlign)
3876      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3877      */
3878     show : function(el, pos, parentMenu)
3879     {
3880         if (false === this.fireEvent("beforeshow", this)) {
3881             Roo.log("show canceled");
3882             return;
3883         }
3884         this.parentMenu = parentMenu;
3885         if(!this.el){
3886             this.render();
3887         }
3888         this.el.addClass('show'); // show otherwise we do not know how big we are..
3889          
3890         var xy = this.el.getAlignToXY(el, pos);
3891         
3892         // bl-tl << left align  below
3893         // tl-bl << left align 
3894         
3895         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3896             // if it goes to far to the right.. -> align left.
3897             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3898         }
3899         if(xy[0] < 0){
3900             // was left align - go right?
3901             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3902         }
3903         
3904         // goes down the bottom
3905         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3906            xy[1]  < 0 ){
3907             var a = this.align.replace('?', '').split('-');
3908             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3909             
3910         }
3911         
3912         this.showAt(  xy , parentMenu, false);
3913     },
3914      /**
3915      * Displays this menu at a specific xy position
3916      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3917      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3918      */
3919     showAt : function(xy, parentMenu, /* private: */_e){
3920         this.parentMenu = parentMenu;
3921         if(!this.el){
3922             this.render();
3923         }
3924         if(_e !== false){
3925             this.fireEvent("beforeshow", this);
3926             //xy = this.el.adjustForConstraints(xy);
3927         }
3928         
3929         //this.el.show();
3930         this.hideMenuItems();
3931         this.hidden = false;
3932         if (this.triggerEl) {
3933             this.triggerEl.addClass('open');
3934         }
3935         
3936         this.el.addClass('show');
3937         
3938         
3939         
3940         // reassign x when hitting right
3941         
3942         // reassign y when hitting bottom
3943         
3944         // but the list may align on trigger left or trigger top... should it be a properity?
3945         
3946         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3947             this.el.setXY(xy);
3948         }
3949         
3950         this.focus();
3951         this.fireEvent("show", this);
3952     },
3953     
3954     focus : function(){
3955         return;
3956         if(!this.hidden){
3957             this.doFocus.defer(50, this);
3958         }
3959     },
3960
3961     doFocus : function(){
3962         if(!this.hidden){
3963             this.focusEl.focus();
3964         }
3965     },
3966
3967     /**
3968      * Hides this menu and optionally all parent menus
3969      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3970      */
3971     hide : function(deep)
3972     {
3973         if (false === this.fireEvent("beforehide", this)) {
3974             Roo.log("hide canceled");
3975             return;
3976         }
3977         this.hideMenuItems();
3978         if(this.el && this.isVisible()){
3979            
3980             if(this.activeItem){
3981                 this.activeItem.deactivate();
3982                 this.activeItem = null;
3983             }
3984             if (this.triggerEl) {
3985                 this.triggerEl.removeClass('open');
3986             }
3987             
3988             this.el.removeClass('show');
3989             this.hidden = true;
3990             this.fireEvent("hide", this);
3991         }
3992         if(deep === true && this.parentMenu){
3993             this.parentMenu.hide(true);
3994         }
3995     },
3996     
3997     onTriggerClick : function(e)
3998     {
3999         Roo.log('trigger click');
4000         
4001         var target = e.getTarget();
4002         
4003         Roo.log(target.nodeName.toLowerCase());
4004         
4005         if(target.nodeName.toLowerCase() === 'i'){
4006             e.preventDefault();
4007         }
4008         
4009     },
4010     
4011     onTriggerPress  : function(e)
4012     {
4013         Roo.log('trigger press');
4014         //Roo.log(e.getTarget());
4015        // Roo.log(this.triggerEl.dom);
4016        
4017         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4018         var pel = Roo.get(e.getTarget());
4019         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4020             Roo.log('is treeview or dropdown?');
4021             return;
4022         }
4023         
4024         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4025             return;
4026         }
4027         
4028         if (this.isVisible()) {
4029             Roo.log('hide');
4030             this.hide();
4031         } else {
4032             Roo.log('show');
4033             
4034             this.show(this.triggerEl, this.align, false);
4035         }
4036         
4037         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4038             e.stopEvent();
4039         }
4040         
4041     },
4042        
4043     
4044     hideMenuItems : function()
4045     {
4046         Roo.log("hide Menu Items");
4047         if (!this.el) { 
4048             return;
4049         }
4050         
4051         this.el.select('.open',true).each(function(aa) {
4052             
4053             aa.removeClass('open');
4054          
4055         });
4056     },
4057     addxtypeChild : function (tree, cntr) {
4058         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4059           
4060         this.menuitems.add(comp);
4061         return comp;
4062
4063     },
4064     getEl : function()
4065     {
4066         Roo.log(this.el);
4067         return this.el;
4068     },
4069     
4070     clear : function()
4071     {
4072         this.getEl().dom.innerHTML = '';
4073         this.menuitems.clear();
4074     }
4075 });
4076
4077  
4078  /*
4079  * - LGPL
4080  *
4081  * menu item
4082  * 
4083  */
4084
4085
4086 /**
4087  * @class Roo.bootstrap.MenuItem
4088  * @extends Roo.bootstrap.Component
4089  * Bootstrap MenuItem class
4090  * @cfg {String} html the menu label
4091  * @cfg {String} href the link
4092  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4093  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4094  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4095  * @cfg {String} fa favicon to show on left of menu item.
4096  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4097  * 
4098  * 
4099  * @constructor
4100  * Create a new MenuItem
4101  * @param {Object} config The config object
4102  */
4103
4104
4105 Roo.bootstrap.MenuItem = function(config){
4106     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4107     this.addEvents({
4108         // raw events
4109         /**
4110          * @event click
4111          * The raw click event for the entire grid.
4112          * @param {Roo.bootstrap.MenuItem} this
4113          * @param {Roo.EventObject} e
4114          */
4115         "click" : true
4116     });
4117 };
4118
4119 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4120     
4121     href : false,
4122     html : false,
4123     preventDefault: false,
4124     isContainer : false,
4125     active : false,
4126     fa: false,
4127     
4128     getAutoCreate : function(){
4129         
4130         if(this.isContainer){
4131             return {
4132                 tag: 'li',
4133                 cls: 'dropdown-menu-item '
4134             };
4135         }
4136         var ctag = {
4137             tag: 'span',
4138             html: 'Link'
4139         };
4140         
4141         var anc = {
4142             tag : 'a',
4143             cls : 'dropdown-item',
4144             href : '#',
4145             cn : [  ]
4146         };
4147         
4148         if (this.fa !== false) {
4149             anc.cn.push({
4150                 tag : 'i',
4151                 cls : 'fa fa-' + this.fa
4152             });
4153         }
4154         
4155         anc.cn.push(ctag);
4156         
4157         
4158         var cfg= {
4159             tag: 'li',
4160             cls: 'dropdown-menu-item',
4161             cn: [ anc ]
4162         };
4163         if (this.parent().type == 'treeview') {
4164             cfg.cls = 'treeview-menu';
4165         }
4166         if (this.active) {
4167             cfg.cls += ' active';
4168         }
4169         
4170         
4171         
4172         anc.href = this.href || cfg.cn[0].href ;
4173         ctag.html = this.html || cfg.cn[0].html ;
4174         return cfg;
4175     },
4176     
4177     initEvents: function()
4178     {
4179         if (this.parent().type == 'treeview') {
4180             this.el.select('a').on('click', this.onClick, this);
4181         }
4182         
4183         if (this.menu) {
4184             this.menu.parentType = this.xtype;
4185             this.menu.triggerEl = this.el;
4186             this.menu = this.addxtype(Roo.apply({}, this.menu));
4187         }
4188         
4189     },
4190     onClick : function(e)
4191     {
4192         Roo.log('item on click ');
4193         
4194         if(this.preventDefault){
4195             e.preventDefault();
4196         }
4197         //this.parent().hideMenuItems();
4198         
4199         this.fireEvent('click', this, e);
4200     },
4201     getEl : function()
4202     {
4203         return this.el;
4204     } 
4205 });
4206
4207  
4208
4209  /*
4210  * - LGPL
4211  *
4212  * menu separator
4213  * 
4214  */
4215
4216
4217 /**
4218  * @class Roo.bootstrap.MenuSeparator
4219  * @extends Roo.bootstrap.Component
4220  * Bootstrap MenuSeparator class
4221  * 
4222  * @constructor
4223  * Create a new MenuItem
4224  * @param {Object} config The config object
4225  */
4226
4227
4228 Roo.bootstrap.MenuSeparator = function(config){
4229     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4230 };
4231
4232 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4233     
4234     getAutoCreate : function(){
4235         var cfg = {
4236             cls: 'divider',
4237             tag : 'li'
4238         };
4239         
4240         return cfg;
4241     }
4242    
4243 });
4244
4245  
4246
4247  
4248 /*
4249 * Licence: LGPL
4250 */
4251
4252 /**
4253  * @class Roo.bootstrap.Modal
4254  * @extends Roo.bootstrap.Component
4255  * Bootstrap Modal class
4256  * @cfg {String} title Title of dialog
4257  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4258  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4259  * @cfg {Boolean} specificTitle default false
4260  * @cfg {Array} buttons Array of buttons or standard button set..
4261  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4262  * @cfg {Boolean} animate default true
4263  * @cfg {Boolean} allow_close default true
4264  * @cfg {Boolean} fitwindow default false
4265  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4266  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4267  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4268  * @cfg {String} size (sm|lg|xl) default empty
4269  * @cfg {Number} max_width set the max width of modal
4270  * @cfg {Boolean} editableTitle can the title be edited
4271
4272  *
4273  *
4274  * @constructor
4275  * Create a new Modal Dialog
4276  * @param {Object} config The config object
4277  */
4278
4279 Roo.bootstrap.Modal = function(config){
4280     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4281     this.addEvents({
4282         // raw events
4283         /**
4284          * @event btnclick
4285          * The raw btnclick event for the button
4286          * @param {Roo.EventObject} e
4287          */
4288         "btnclick" : true,
4289         /**
4290          * @event resize
4291          * Fire when dialog resize
4292          * @param {Roo.bootstrap.Modal} this
4293          * @param {Roo.EventObject} e
4294          */
4295         "resize" : true,
4296         /**
4297          * @event titlechanged
4298          * Fire when the editable title has been changed
4299          * @param {Roo.bootstrap.Modal} this
4300          * @param {Roo.EventObject} value
4301          */
4302         "titlechanged" : true 
4303         
4304     });
4305     this.buttons = this.buttons || [];
4306
4307     if (this.tmpl) {
4308         this.tmpl = Roo.factory(this.tmpl);
4309     }
4310
4311 };
4312
4313 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4314
4315     title : 'test dialog',
4316
4317     buttons : false,
4318
4319     // set on load...
4320
4321     html: false,
4322
4323     tmp: false,
4324
4325     specificTitle: false,
4326
4327     buttonPosition: 'right',
4328
4329     allow_close : true,
4330
4331     animate : true,
4332
4333     fitwindow: false,
4334     
4335      // private
4336     dialogEl: false,
4337     bodyEl:  false,
4338     footerEl:  false,
4339     titleEl:  false,
4340     closeEl:  false,
4341
4342     size: '',
4343     
4344     max_width: 0,
4345     
4346     max_height: 0,
4347     
4348     fit_content: false,
4349     editableTitle  : false,
4350
4351     onRender : function(ct, position)
4352     {
4353         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4354
4355         if(!this.el){
4356             var cfg = Roo.apply({},  this.getAutoCreate());
4357             cfg.id = Roo.id();
4358             //if(!cfg.name){
4359             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4360             //}
4361             //if (!cfg.name.length) {
4362             //    delete cfg.name;
4363            // }
4364             if (this.cls) {
4365                 cfg.cls += ' ' + this.cls;
4366             }
4367             if (this.style) {
4368                 cfg.style = this.style;
4369             }
4370             this.el = Roo.get(document.body).createChild(cfg, position);
4371         }
4372         //var type = this.el.dom.type;
4373
4374
4375         if(this.tabIndex !== undefined){
4376             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4377         }
4378
4379         this.dialogEl = this.el.select('.modal-dialog',true).first();
4380         this.bodyEl = this.el.select('.modal-body',true).first();
4381         this.closeEl = this.el.select('.modal-header .close', true).first();
4382         this.headerEl = this.el.select('.modal-header',true).first();
4383         this.titleEl = this.el.select('.modal-title',true).first();
4384         this.footerEl = this.el.select('.modal-footer',true).first();
4385
4386         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4387         
4388         //this.el.addClass("x-dlg-modal");
4389
4390         if (this.buttons.length) {
4391             Roo.each(this.buttons, function(bb) {
4392                 var b = Roo.apply({}, bb);
4393                 b.xns = b.xns || Roo.bootstrap;
4394                 b.xtype = b.xtype || 'Button';
4395                 if (typeof(b.listeners) == 'undefined') {
4396                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4397                 }
4398
4399                 var btn = Roo.factory(b);
4400
4401                 btn.render(this.getButtonContainer());
4402
4403             },this);
4404         }
4405         // render the children.
4406         var nitems = [];
4407
4408         if(typeof(this.items) != 'undefined'){
4409             var items = this.items;
4410             delete this.items;
4411
4412             for(var i =0;i < items.length;i++) {
4413                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4414             }
4415         }
4416
4417         this.items = nitems;
4418
4419         // where are these used - they used to be body/close/footer
4420
4421
4422         this.initEvents();
4423         //this.el.addClass([this.fieldClass, this.cls]);
4424
4425     },
4426
4427     getAutoCreate : function()
4428     {
4429         // we will default to modal-body-overflow - might need to remove or make optional later.
4430         var bdy = {
4431                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4432                 html : this.html || ''
4433         };
4434
4435         var title = {
4436             tag: 'h5',
4437             cls : 'modal-title',
4438             html : this.title
4439         };
4440
4441         if(this.specificTitle){ // WTF is this?
4442             title = this.title;
4443         }
4444
4445         var header = [];
4446         if (this.allow_close && Roo.bootstrap.version == 3) {
4447             header.push({
4448                 tag: 'button',
4449                 cls : 'close',
4450                 html : '&times'
4451             });
4452         }
4453
4454         header.push(title);
4455
4456         if (this.editableTitle) {
4457             header.push({
4458                 cls: 'form-control roo-editable-title d-none',
4459                 tag: 'input',
4460                 type: 'text'
4461             });
4462         }
4463         
4464         if (this.allow_close && Roo.bootstrap.version == 4) {
4465             header.push({
4466                 tag: 'button',
4467                 cls : 'close',
4468                 html : '&times'
4469             });
4470         }
4471         
4472         var size = '';
4473
4474         if(this.size.length){
4475             size = 'modal-' + this.size;
4476         }
4477         
4478         var footer = Roo.bootstrap.version == 3 ?
4479             {
4480                 cls : 'modal-footer',
4481                 cn : [
4482                     {
4483                         tag: 'div',
4484                         cls: 'btn-' + this.buttonPosition
4485                     }
4486                 ]
4487
4488             } :
4489             {  // BS4 uses mr-auto on left buttons....
4490                 cls : 'modal-footer'
4491             };
4492
4493             
4494
4495         
4496         
4497         var modal = {
4498             cls: "modal",
4499              cn : [
4500                 {
4501                     cls: "modal-dialog " + size,
4502                     cn : [
4503                         {
4504                             cls : "modal-content",
4505                             cn : [
4506                                 {
4507                                     cls : 'modal-header',
4508                                     cn : header
4509                                 },
4510                                 bdy,
4511                                 footer
4512                             ]
4513
4514                         }
4515                     ]
4516
4517                 }
4518             ]
4519         };
4520
4521         if(this.animate){
4522             modal.cls += ' fade';
4523         }
4524
4525         return modal;
4526
4527     },
4528     getChildContainer : function() {
4529
4530          return this.bodyEl;
4531
4532     },
4533     getButtonContainer : function() {
4534         
4535          return Roo.bootstrap.version == 4 ?
4536             this.el.select('.modal-footer',true).first()
4537             : this.el.select('.modal-footer div',true).first();
4538
4539     },
4540     initEvents : function()
4541     {
4542         if (this.allow_close) {
4543             this.closeEl.on('click', this.hide, this);
4544         }
4545         Roo.EventManager.onWindowResize(this.resize, this, true);
4546         if (this.editableTitle) {
4547             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4548             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4549             this.headerEditEl.on('keyup', function(e) {
4550                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4551                         this.toggleHeaderInput(false)
4552                     }
4553                 }, this);
4554             this.headerEditEl.on('blur', function(e) {
4555                 this.toggleHeaderInput(false)
4556             },this);
4557         }
4558
4559     },
4560   
4561
4562     resize : function()
4563     {
4564         this.maskEl.setSize(
4565             Roo.lib.Dom.getViewWidth(true),
4566             Roo.lib.Dom.getViewHeight(true)
4567         );
4568         
4569         if (this.fitwindow) {
4570             
4571            this.dialogEl.setStyle( { 'max-width' : '100%' });
4572             this.setSize(
4573                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4574                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4575             );
4576             return;
4577         }
4578         
4579         if(this.max_width !== 0) {
4580             
4581             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4582             
4583             if(this.height) {
4584                 this.setSize(w, this.height);
4585                 return;
4586             }
4587             
4588             if(this.max_height) {
4589                 this.setSize(w,Math.min(
4590                     this.max_height,
4591                     Roo.lib.Dom.getViewportHeight(true) - 60
4592                 ));
4593                 
4594                 return;
4595             }
4596             
4597             if(!this.fit_content) {
4598                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4599                 return;
4600             }
4601             
4602             this.setSize(w, Math.min(
4603                 60 +
4604                 this.headerEl.getHeight() + 
4605                 this.footerEl.getHeight() + 
4606                 this.getChildHeight(this.bodyEl.dom.childNodes),
4607                 Roo.lib.Dom.getViewportHeight(true) - 60)
4608             );
4609         }
4610         
4611     },
4612
4613     setSize : function(w,h)
4614     {
4615         if (!w && !h) {
4616             return;
4617         }
4618         
4619         this.resizeTo(w,h);
4620     },
4621
4622     show : function() {
4623
4624         if (!this.rendered) {
4625             this.render();
4626         }
4627         this.toggleHeaderInput(false);
4628         //this.el.setStyle('display', 'block');
4629         this.el.removeClass('hideing');
4630         this.el.dom.style.display='block';
4631         
4632         Roo.get(document.body).addClass('modal-open');
4633  
4634         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4635             
4636             (function(){
4637                 this.el.addClass('show');
4638                 this.el.addClass('in');
4639             }).defer(50, this);
4640         }else{
4641             this.el.addClass('show');
4642             this.el.addClass('in');
4643         }
4644
4645         // not sure how we can show data in here..
4646         //if (this.tmpl) {
4647         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4648         //}
4649
4650         Roo.get(document.body).addClass("x-body-masked");
4651         
4652         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4653         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4654         this.maskEl.dom.style.display = 'block';
4655         this.maskEl.addClass('show');
4656         
4657         
4658         this.resize();
4659         
4660         this.fireEvent('show', this);
4661
4662         // set zindex here - otherwise it appears to be ignored...
4663         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4664
4665         (function () {
4666             this.items.forEach( function(e) {
4667                 e.layout ? e.layout() : false;
4668
4669             });
4670         }).defer(100,this);
4671
4672     },
4673     hide : function()
4674     {
4675         if(this.fireEvent("beforehide", this) !== false){
4676             
4677             this.maskEl.removeClass('show');
4678             
4679             this.maskEl.dom.style.display = '';
4680             Roo.get(document.body).removeClass("x-body-masked");
4681             this.el.removeClass('in');
4682             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4683
4684             if(this.animate){ // why
4685                 this.el.addClass('hideing');
4686                 this.el.removeClass('show');
4687                 (function(){
4688                     if (!this.el.hasClass('hideing')) {
4689                         return; // it's been shown again...
4690                     }
4691                     
4692                     this.el.dom.style.display='';
4693
4694                     Roo.get(document.body).removeClass('modal-open');
4695                     this.el.removeClass('hideing');
4696                 }).defer(150,this);
4697                 
4698             }else{
4699                 this.el.removeClass('show');
4700                 this.el.dom.style.display='';
4701                 Roo.get(document.body).removeClass('modal-open');
4702
4703             }
4704             this.fireEvent('hide', this);
4705         }
4706     },
4707     isVisible : function()
4708     {
4709         
4710         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4711         
4712     },
4713
4714     addButton : function(str, cb)
4715     {
4716
4717
4718         var b = Roo.apply({}, { html : str } );
4719         b.xns = b.xns || Roo.bootstrap;
4720         b.xtype = b.xtype || 'Button';
4721         if (typeof(b.listeners) == 'undefined') {
4722             b.listeners = { click : cb.createDelegate(this)  };
4723         }
4724
4725         var btn = Roo.factory(b);
4726
4727         btn.render(this.getButtonContainer());
4728
4729         return btn;
4730
4731     },
4732
4733     setDefaultButton : function(btn)
4734     {
4735         //this.el.select('.modal-footer').()
4736     },
4737
4738     resizeTo: function(w,h)
4739     {
4740         this.dialogEl.setWidth(w);
4741         
4742         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4743
4744         this.bodyEl.setHeight(h - diff);
4745         
4746         this.fireEvent('resize', this);
4747     },
4748     
4749     setContentSize  : function(w, h)
4750     {
4751
4752     },
4753     onButtonClick: function(btn,e)
4754     {
4755         //Roo.log([a,b,c]);
4756         this.fireEvent('btnclick', btn.name, e);
4757     },
4758      /**
4759      * Set the title of the Dialog
4760      * @param {String} str new Title
4761      */
4762     setTitle: function(str) {
4763         this.titleEl.dom.innerHTML = str;
4764         this.title = str;
4765     },
4766     /**
4767      * Set the body of the Dialog
4768      * @param {String} str new Title
4769      */
4770     setBody: function(str) {
4771         this.bodyEl.dom.innerHTML = str;
4772     },
4773     /**
4774      * Set the body of the Dialog using the template
4775      * @param {Obj} data - apply this data to the template and replace the body contents.
4776      */
4777     applyBody: function(obj)
4778     {
4779         if (!this.tmpl) {
4780             Roo.log("Error - using apply Body without a template");
4781             //code
4782         }
4783         this.tmpl.overwrite(this.bodyEl, obj);
4784     },
4785     
4786     getChildHeight : function(child_nodes)
4787     {
4788         if(
4789             !child_nodes ||
4790             child_nodes.length == 0
4791         ) {
4792             return 0;
4793         }
4794         
4795         var child_height = 0;
4796         
4797         for(var i = 0; i < child_nodes.length; i++) {
4798             
4799             /*
4800             * for modal with tabs...
4801             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4802                 
4803                 var layout_childs = child_nodes[i].childNodes;
4804                 
4805                 for(var j = 0; j < layout_childs.length; j++) {
4806                     
4807                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4808                         
4809                         var layout_body_childs = layout_childs[j].childNodes;
4810                         
4811                         for(var k = 0; k < layout_body_childs.length; k++) {
4812                             
4813                             if(layout_body_childs[k].classList.contains('navbar')) {
4814                                 child_height += layout_body_childs[k].offsetHeight;
4815                                 continue;
4816                             }
4817                             
4818                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4819                                 
4820                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4821                                 
4822                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4823                                     
4824                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4825                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4826                                         continue;
4827                                     }
4828                                     
4829                                 }
4830                                 
4831                             }
4832                             
4833                         }
4834                     }
4835                 }
4836                 continue;
4837             }
4838             */
4839             
4840             child_height += child_nodes[i].offsetHeight;
4841             // Roo.log(child_nodes[i].offsetHeight);
4842         }
4843         
4844         return child_height;
4845     },
4846     toggleHeaderInput : function(is_edit)
4847     {
4848         if (!this.editableTitle) {
4849             return; // not editable.
4850         }
4851         if (is_edit && this.is_header_editing) {
4852             return; // already editing..
4853         }
4854         if (is_edit) {
4855     
4856             this.headerEditEl.dom.value = this.title;
4857             this.headerEditEl.removeClass('d-none');
4858             this.headerEditEl.dom.focus();
4859             this.titleEl.addClass('d-none');
4860             
4861             this.is_header_editing = true;
4862             return
4863         }
4864         // flip back to not editing.
4865         this.title = this.headerEditEl.dom.value;
4866         this.headerEditEl.addClass('d-none');
4867         this.titleEl.removeClass('d-none');
4868         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4869         this.is_header_editing = false;
4870         this.fireEvent('titlechanged', this, this.title);
4871     
4872             
4873         
4874     }
4875
4876 });
4877
4878
4879 Roo.apply(Roo.bootstrap.Modal,  {
4880     /**
4881          * Button config that displays a single OK button
4882          * @type Object
4883          */
4884         OK :  [{
4885             name : 'ok',
4886             weight : 'primary',
4887             html : 'OK'
4888         }],
4889         /**
4890          * Button config that displays Yes and No buttons
4891          * @type Object
4892          */
4893         YESNO : [
4894             {
4895                 name  : 'no',
4896                 html : 'No'
4897             },
4898             {
4899                 name  :'yes',
4900                 weight : 'primary',
4901                 html : 'Yes'
4902             }
4903         ],
4904
4905         /**
4906          * Button config that displays OK and Cancel buttons
4907          * @type Object
4908          */
4909         OKCANCEL : [
4910             {
4911                name : 'cancel',
4912                 html : 'Cancel'
4913             },
4914             {
4915                 name : 'ok',
4916                 weight : 'primary',
4917                 html : 'OK'
4918             }
4919         ],
4920         /**
4921          * Button config that displays Yes, No and Cancel buttons
4922          * @type Object
4923          */
4924         YESNOCANCEL : [
4925             {
4926                 name : 'yes',
4927                 weight : 'primary',
4928                 html : 'Yes'
4929             },
4930             {
4931                 name : 'no',
4932                 html : 'No'
4933             },
4934             {
4935                 name : 'cancel',
4936                 html : 'Cancel'
4937             }
4938         ],
4939         
4940         zIndex : 10001
4941 });
4942
4943 /*
4944  * - LGPL
4945  *
4946  * messagebox - can be used as a replace
4947  * 
4948  */
4949 /**
4950  * @class Roo.MessageBox
4951  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4952  * Example usage:
4953  *<pre><code>
4954 // Basic alert:
4955 Roo.Msg.alert('Status', 'Changes saved successfully.');
4956
4957 // Prompt for user data:
4958 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4959     if (btn == 'ok'){
4960         // process text value...
4961     }
4962 });
4963
4964 // Show a dialog using config options:
4965 Roo.Msg.show({
4966    title:'Save Changes?',
4967    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4968    buttons: Roo.Msg.YESNOCANCEL,
4969    fn: processResult,
4970    animEl: 'elId'
4971 });
4972 </code></pre>
4973  * @singleton
4974  */
4975 Roo.bootstrap.MessageBox = function(){
4976     var dlg, opt, mask, waitTimer;
4977     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4978     var buttons, activeTextEl, bwidth;
4979
4980     
4981     // private
4982     var handleButton = function(button){
4983         dlg.hide();
4984         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4985     };
4986
4987     // private
4988     var handleHide = function(){
4989         if(opt && opt.cls){
4990             dlg.el.removeClass(opt.cls);
4991         }
4992         //if(waitTimer){
4993         //    Roo.TaskMgr.stop(waitTimer);
4994         //    waitTimer = null;
4995         //}
4996     };
4997
4998     // private
4999     var updateButtons = function(b){
5000         var width = 0;
5001         if(!b){
5002             buttons["ok"].hide();
5003             buttons["cancel"].hide();
5004             buttons["yes"].hide();
5005             buttons["no"].hide();
5006             dlg.footerEl.hide();
5007             
5008             return width;
5009         }
5010         dlg.footerEl.show();
5011         for(var k in buttons){
5012             if(typeof buttons[k] != "function"){
5013                 if(b[k]){
5014                     buttons[k].show();
5015                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5016                     width += buttons[k].el.getWidth()+15;
5017                 }else{
5018                     buttons[k].hide();
5019                 }
5020             }
5021         }
5022         return width;
5023     };
5024
5025     // private
5026     var handleEsc = function(d, k, e){
5027         if(opt && opt.closable !== false){
5028             dlg.hide();
5029         }
5030         if(e){
5031             e.stopEvent();
5032         }
5033     };
5034
5035     return {
5036         /**
5037          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5038          * @return {Roo.BasicDialog} The BasicDialog element
5039          */
5040         getDialog : function(){
5041            if(!dlg){
5042                 dlg = new Roo.bootstrap.Modal( {
5043                     //draggable: true,
5044                     //resizable:false,
5045                     //constraintoviewport:false,
5046                     //fixedcenter:true,
5047                     //collapsible : false,
5048                     //shim:true,
5049                     //modal: true,
5050                 //    width: 'auto',
5051                   //  height:100,
5052                     //buttonAlign:"center",
5053                     closeClick : function(){
5054                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5055                             handleButton("no");
5056                         }else{
5057                             handleButton("cancel");
5058                         }
5059                     }
5060                 });
5061                 dlg.render();
5062                 dlg.on("hide", handleHide);
5063                 mask = dlg.mask;
5064                 //dlg.addKeyListener(27, handleEsc);
5065                 buttons = {};
5066                 this.buttons = buttons;
5067                 var bt = this.buttonText;
5068                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5069                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5070                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5071                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5072                 //Roo.log(buttons);
5073                 bodyEl = dlg.bodyEl.createChild({
5074
5075                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5076                         '<textarea class="roo-mb-textarea"></textarea>' +
5077                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5078                 });
5079                 msgEl = bodyEl.dom.firstChild;
5080                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5081                 textboxEl.enableDisplayMode();
5082                 textboxEl.addKeyListener([10,13], function(){
5083                     if(dlg.isVisible() && opt && opt.buttons){
5084                         if(opt.buttons.ok){
5085                             handleButton("ok");
5086                         }else if(opt.buttons.yes){
5087                             handleButton("yes");
5088                         }
5089                     }
5090                 });
5091                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5092                 textareaEl.enableDisplayMode();
5093                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5094                 progressEl.enableDisplayMode();
5095                 
5096                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5097                 var pf = progressEl.dom.firstChild;
5098                 if (pf) {
5099                     pp = Roo.get(pf.firstChild);
5100                     pp.setHeight(pf.offsetHeight);
5101                 }
5102                 
5103             }
5104             return dlg;
5105         },
5106
5107         /**
5108          * Updates the message box body text
5109          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5110          * the XHTML-compliant non-breaking space character '&amp;#160;')
5111          * @return {Roo.MessageBox} This message box
5112          */
5113         updateText : function(text)
5114         {
5115             if(!dlg.isVisible() && !opt.width){
5116                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5117                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5118             }
5119             msgEl.innerHTML = text || '&#160;';
5120       
5121             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5122             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5123             var w = Math.max(
5124                     Math.min(opt.width || cw , this.maxWidth), 
5125                     Math.max(opt.minWidth || this.minWidth, bwidth)
5126             );
5127             if(opt.prompt){
5128                 activeTextEl.setWidth(w);
5129             }
5130             if(dlg.isVisible()){
5131                 dlg.fixedcenter = false;
5132             }
5133             // to big, make it scroll. = But as usual stupid IE does not support
5134             // !important..
5135             
5136             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5137                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5138                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5139             } else {
5140                 bodyEl.dom.style.height = '';
5141                 bodyEl.dom.style.overflowY = '';
5142             }
5143             if (cw > w) {
5144                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5145             } else {
5146                 bodyEl.dom.style.overflowX = '';
5147             }
5148             
5149             dlg.setContentSize(w, bodyEl.getHeight());
5150             if(dlg.isVisible()){
5151                 dlg.fixedcenter = true;
5152             }
5153             return this;
5154         },
5155
5156         /**
5157          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5158          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5159          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5160          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5161          * @return {Roo.MessageBox} This message box
5162          */
5163         updateProgress : function(value, text){
5164             if(text){
5165                 this.updateText(text);
5166             }
5167             
5168             if (pp) { // weird bug on my firefox - for some reason this is not defined
5169                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5170                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5171             }
5172             return this;
5173         },        
5174
5175         /**
5176          * Returns true if the message box is currently displayed
5177          * @return {Boolean} True if the message box is visible, else false
5178          */
5179         isVisible : function(){
5180             return dlg && dlg.isVisible();  
5181         },
5182
5183         /**
5184          * Hides the message box if it is displayed
5185          */
5186         hide : function(){
5187             if(this.isVisible()){
5188                 dlg.hide();
5189             }  
5190         },
5191
5192         /**
5193          * Displays a new message box, or reinitializes an existing message box, based on the config options
5194          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5195          * The following config object properties are supported:
5196          * <pre>
5197 Property    Type             Description
5198 ----------  ---------------  ------------------------------------------------------------------------------------
5199 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5200                                    closes (defaults to undefined)
5201 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5202                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5203 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5204                                    progress and wait dialogs will ignore this property and always hide the
5205                                    close button as they can only be closed programmatically.
5206 cls               String           A custom CSS class to apply to the message box element
5207 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5208                                    displayed (defaults to 75)
5209 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5210                                    function will be btn (the name of the button that was clicked, if applicable,
5211                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5212                                    Progress and wait dialogs will ignore this option since they do not respond to
5213                                    user actions and can only be closed programmatically, so any required function
5214                                    should be called by the same code after it closes the dialog.
5215 icon              String           A CSS class that provides a background image to be used as an icon for
5216                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5217 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5218 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5219 modal             Boolean          False to allow user interaction with the page while the message box is
5220                                    displayed (defaults to true)
5221 msg               String           A string that will replace the existing message box body text (defaults
5222                                    to the XHTML-compliant non-breaking space character '&#160;')
5223 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5224 progress          Boolean          True to display a progress bar (defaults to false)
5225 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5226 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5227 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5228 title             String           The title text
5229 value             String           The string value to set into the active textbox element if displayed
5230 wait              Boolean          True to display a progress bar (defaults to false)
5231 width             Number           The width of the dialog in pixels
5232 </pre>
5233          *
5234          * Example usage:
5235          * <pre><code>
5236 Roo.Msg.show({
5237    title: 'Address',
5238    msg: 'Please enter your address:',
5239    width: 300,
5240    buttons: Roo.MessageBox.OKCANCEL,
5241    multiline: true,
5242    fn: saveAddress,
5243    animEl: 'addAddressBtn'
5244 });
5245 </code></pre>
5246          * @param {Object} config Configuration options
5247          * @return {Roo.MessageBox} This message box
5248          */
5249         show : function(options)
5250         {
5251             
5252             // this causes nightmares if you show one dialog after another
5253             // especially on callbacks..
5254              
5255             if(this.isVisible()){
5256                 
5257                 this.hide();
5258                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5259                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5260                 Roo.log("New Dialog Message:" +  options.msg )
5261                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5262                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5263                 
5264             }
5265             var d = this.getDialog();
5266             opt = options;
5267             d.setTitle(opt.title || "&#160;");
5268             d.closeEl.setDisplayed(opt.closable !== false);
5269             activeTextEl = textboxEl;
5270             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5271             if(opt.prompt){
5272                 if(opt.multiline){
5273                     textboxEl.hide();
5274                     textareaEl.show();
5275                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5276                         opt.multiline : this.defaultTextHeight);
5277                     activeTextEl = textareaEl;
5278                 }else{
5279                     textboxEl.show();
5280                     textareaEl.hide();
5281                 }
5282             }else{
5283                 textboxEl.hide();
5284                 textareaEl.hide();
5285             }
5286             progressEl.setDisplayed(opt.progress === true);
5287             if (opt.progress) {
5288                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5289             }
5290             this.updateProgress(0);
5291             activeTextEl.dom.value = opt.value || "";
5292             if(opt.prompt){
5293                 dlg.setDefaultButton(activeTextEl);
5294             }else{
5295                 var bs = opt.buttons;
5296                 var db = null;
5297                 if(bs && bs.ok){
5298                     db = buttons["ok"];
5299                 }else if(bs && bs.yes){
5300                     db = buttons["yes"];
5301                 }
5302                 dlg.setDefaultButton(db);
5303             }
5304             bwidth = updateButtons(opt.buttons);
5305             this.updateText(opt.msg);
5306             if(opt.cls){
5307                 d.el.addClass(opt.cls);
5308             }
5309             d.proxyDrag = opt.proxyDrag === true;
5310             d.modal = opt.modal !== false;
5311             d.mask = opt.modal !== false ? mask : false;
5312             if(!d.isVisible()){
5313                 // force it to the end of the z-index stack so it gets a cursor in FF
5314                 document.body.appendChild(dlg.el.dom);
5315                 d.animateTarget = null;
5316                 d.show(options.animEl);
5317             }
5318             return this;
5319         },
5320
5321         /**
5322          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5323          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5324          * and closing the message box when the process is complete.
5325          * @param {String} title The title bar text
5326          * @param {String} msg The message box body text
5327          * @return {Roo.MessageBox} This message box
5328          */
5329         progress : function(title, msg){
5330             this.show({
5331                 title : title,
5332                 msg : msg,
5333                 buttons: false,
5334                 progress:true,
5335                 closable:false,
5336                 minWidth: this.minProgressWidth,
5337                 modal : true
5338             });
5339             return this;
5340         },
5341
5342         /**
5343          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5344          * If a callback function is passed it will be called after the user clicks the button, and the
5345          * id of the button that was clicked will be passed as the only parameter to the callback
5346          * (could also be the top-right close button).
5347          * @param {String} title The title bar text
5348          * @param {String} msg The message box body text
5349          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5350          * @param {Object} scope (optional) The scope of the callback function
5351          * @return {Roo.MessageBox} This message box
5352          */
5353         alert : function(title, msg, fn, scope)
5354         {
5355             this.show({
5356                 title : title,
5357                 msg : msg,
5358                 buttons: this.OK,
5359                 fn: fn,
5360                 closable : false,
5361                 scope : scope,
5362                 modal : true
5363             });
5364             return this;
5365         },
5366
5367         /**
5368          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5369          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5370          * You are responsible for closing the message box when the process is complete.
5371          * @param {String} msg The message box body text
5372          * @param {String} title (optional) The title bar text
5373          * @return {Roo.MessageBox} This message box
5374          */
5375         wait : function(msg, title){
5376             this.show({
5377                 title : title,
5378                 msg : msg,
5379                 buttons: false,
5380                 closable:false,
5381                 progress:true,
5382                 modal:true,
5383                 width:300,
5384                 wait:true
5385             });
5386             waitTimer = Roo.TaskMgr.start({
5387                 run: function(i){
5388                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5389                 },
5390                 interval: 1000
5391             });
5392             return this;
5393         },
5394
5395         /**
5396          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5397          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5398          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5399          * @param {String} title The title bar text
5400          * @param {String} msg The message box body text
5401          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5402          * @param {Object} scope (optional) The scope of the callback function
5403          * @return {Roo.MessageBox} This message box
5404          */
5405         confirm : function(title, msg, fn, scope){
5406             this.show({
5407                 title : title,
5408                 msg : msg,
5409                 buttons: this.YESNO,
5410                 fn: fn,
5411                 scope : scope,
5412                 modal : true
5413             });
5414             return this;
5415         },
5416
5417         /**
5418          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5419          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5420          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5421          * (could also be the top-right close button) and the text that was entered will be passed as the two
5422          * parameters to the callback.
5423          * @param {String} title The title bar text
5424          * @param {String} msg The message box body text
5425          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426          * @param {Object} scope (optional) The scope of the callback function
5427          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5428          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5429          * @return {Roo.MessageBox} This message box
5430          */
5431         prompt : function(title, msg, fn, scope, multiline){
5432             this.show({
5433                 title : title,
5434                 msg : msg,
5435                 buttons: this.OKCANCEL,
5436                 fn: fn,
5437                 minWidth:250,
5438                 scope : scope,
5439                 prompt:true,
5440                 multiline: multiline,
5441                 modal : true
5442             });
5443             return this;
5444         },
5445
5446         /**
5447          * Button config that displays a single OK button
5448          * @type Object
5449          */
5450         OK : {ok:true},
5451         /**
5452          * Button config that displays Yes and No buttons
5453          * @type Object
5454          */
5455         YESNO : {yes:true, no:true},
5456         /**
5457          * Button config that displays OK and Cancel buttons
5458          * @type Object
5459          */
5460         OKCANCEL : {ok:true, cancel:true},
5461         /**
5462          * Button config that displays Yes, No and Cancel buttons
5463          * @type Object
5464          */
5465         YESNOCANCEL : {yes:true, no:true, cancel:true},
5466
5467         /**
5468          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5469          * @type Number
5470          */
5471         defaultTextHeight : 75,
5472         /**
5473          * The maximum width in pixels of the message box (defaults to 600)
5474          * @type Number
5475          */
5476         maxWidth : 600,
5477         /**
5478          * The minimum width in pixels of the message box (defaults to 100)
5479          * @type Number
5480          */
5481         minWidth : 100,
5482         /**
5483          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5484          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5485          * @type Number
5486          */
5487         minProgressWidth : 250,
5488         /**
5489          * An object containing the default button text strings that can be overriden for localized language support.
5490          * Supported properties are: ok, cancel, yes and no.
5491          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5492          * @type Object
5493          */
5494         buttonText : {
5495             ok : "OK",
5496             cancel : "Cancel",
5497             yes : "Yes",
5498             no : "No"
5499         }
5500     };
5501 }();
5502
5503 /**
5504  * Shorthand for {@link Roo.MessageBox}
5505  */
5506 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5507 Roo.Msg = Roo.Msg || Roo.MessageBox;
5508 /*
5509  * - LGPL
5510  *
5511  * navbar
5512  * 
5513  */
5514
5515 /**
5516  * @class Roo.bootstrap.Navbar
5517  * @extends Roo.bootstrap.Component
5518  * Bootstrap Navbar class
5519
5520  * @constructor
5521  * Create a new Navbar
5522  * @param {Object} config The config object
5523  */
5524
5525
5526 Roo.bootstrap.Navbar = function(config){
5527     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5528     this.addEvents({
5529         // raw events
5530         /**
5531          * @event beforetoggle
5532          * Fire before toggle the menu
5533          * @param {Roo.EventObject} e
5534          */
5535         "beforetoggle" : true
5536     });
5537 };
5538
5539 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5540     
5541     
5542    
5543     // private
5544     navItems : false,
5545     loadMask : false,
5546     
5547     
5548     getAutoCreate : function(){
5549         
5550         
5551         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5552         
5553     },
5554     
5555     initEvents :function ()
5556     {
5557         //Roo.log(this.el.select('.navbar-toggle',true));
5558         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5559         
5560         var mark = {
5561             tag: "div",
5562             cls:"x-dlg-mask"
5563         };
5564         
5565         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5566         
5567         var size = this.el.getSize();
5568         this.maskEl.setSize(size.width, size.height);
5569         this.maskEl.enableDisplayMode("block");
5570         this.maskEl.hide();
5571         
5572         if(this.loadMask){
5573             this.maskEl.show();
5574         }
5575     },
5576     
5577     
5578     getChildContainer : function()
5579     {
5580         if (this.el && this.el.select('.collapse').getCount()) {
5581             return this.el.select('.collapse',true).first();
5582         }
5583         
5584         return this.el;
5585     },
5586     
5587     mask : function()
5588     {
5589         this.maskEl.show();
5590     },
5591     
5592     unmask : function()
5593     {
5594         this.maskEl.hide();
5595     },
5596     onToggle : function()
5597     {
5598         
5599         if(this.fireEvent('beforetoggle', this) === false){
5600             return;
5601         }
5602         var ce = this.el.select('.navbar-collapse',true).first();
5603       
5604         if (!ce.hasClass('show')) {
5605            this.expand();
5606         } else {
5607             this.collapse();
5608         }
5609         
5610         
5611     
5612     },
5613     /**
5614      * Expand the navbar pulldown 
5615      */
5616     expand : function ()
5617     {
5618        
5619         var ce = this.el.select('.navbar-collapse',true).first();
5620         if (ce.hasClass('collapsing')) {
5621             return;
5622         }
5623         ce.dom.style.height = '';
5624                // show it...
5625         ce.addClass('in'); // old...
5626         ce.removeClass('collapse');
5627         ce.addClass('show');
5628         var h = ce.getHeight();
5629         Roo.log(h);
5630         ce.removeClass('show');
5631         // at this point we should be able to see it..
5632         ce.addClass('collapsing');
5633         
5634         ce.setHeight(0); // resize it ...
5635         ce.on('transitionend', function() {
5636             //Roo.log('done transition');
5637             ce.removeClass('collapsing');
5638             ce.addClass('show');
5639             ce.removeClass('collapse');
5640
5641             ce.dom.style.height = '';
5642         }, this, { single: true} );
5643         ce.setHeight(h);
5644         ce.dom.scrollTop = 0;
5645     },
5646     /**
5647      * Collapse the navbar pulldown 
5648      */
5649     collapse : function()
5650     {
5651          var ce = this.el.select('.navbar-collapse',true).first();
5652        
5653         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5654             // it's collapsed or collapsing..
5655             return;
5656         }
5657         ce.removeClass('in'); // old...
5658         ce.setHeight(ce.getHeight());
5659         ce.removeClass('show');
5660         ce.addClass('collapsing');
5661         
5662         ce.on('transitionend', function() {
5663             ce.dom.style.height = '';
5664             ce.removeClass('collapsing');
5665             ce.addClass('collapse');
5666         }, this, { single: true} );
5667         ce.setHeight(0);
5668     }
5669     
5670     
5671     
5672 });
5673
5674
5675
5676  
5677
5678  /*
5679  * - LGPL
5680  *
5681  * navbar
5682  * 
5683  */
5684
5685 /**
5686  * @class Roo.bootstrap.NavSimplebar
5687  * @extends Roo.bootstrap.Navbar
5688  * Bootstrap Sidebar class
5689  *
5690  * @cfg {Boolean} inverse is inverted color
5691  * 
5692  * @cfg {String} type (nav | pills | tabs)
5693  * @cfg {Boolean} arrangement stacked | justified
5694  * @cfg {String} align (left | right) alignment
5695  * 
5696  * @cfg {Boolean} main (true|false) main nav bar? default false
5697  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5698  * 
5699  * @cfg {String} tag (header|footer|nav|div) default is nav 
5700
5701  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5702  * 
5703  * 
5704  * @constructor
5705  * Create a new Sidebar
5706  * @param {Object} config The config object
5707  */
5708
5709
5710 Roo.bootstrap.NavSimplebar = function(config){
5711     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5712 };
5713
5714 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5715     
5716     inverse: false,
5717     
5718     type: false,
5719     arrangement: '',
5720     align : false,
5721     
5722     weight : 'light',
5723     
5724     main : false,
5725     
5726     
5727     tag : false,
5728     
5729     
5730     getAutoCreate : function(){
5731         
5732         
5733         var cfg = {
5734             tag : this.tag || 'div',
5735             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5736         };
5737         if (['light','white'].indexOf(this.weight) > -1) {
5738             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5739         }
5740         cfg.cls += ' bg-' + this.weight;
5741         
5742         if (this.inverse) {
5743             cfg.cls += ' navbar-inverse';
5744             
5745         }
5746         
5747         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5748         
5749         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5750             return cfg;
5751         }
5752         
5753         
5754     
5755         
5756         cfg.cn = [
5757             {
5758                 cls: 'nav nav-' + this.xtype,
5759                 tag : 'ul'
5760             }
5761         ];
5762         
5763          
5764         this.type = this.type || 'nav';
5765         if (['tabs','pills'].indexOf(this.type) != -1) {
5766             cfg.cn[0].cls += ' nav-' + this.type
5767         
5768         
5769         } else {
5770             if (this.type!=='nav') {
5771                 Roo.log('nav type must be nav/tabs/pills')
5772             }
5773             cfg.cn[0].cls += ' navbar-nav'
5774         }
5775         
5776         
5777         
5778         
5779         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5780             cfg.cn[0].cls += ' nav-' + this.arrangement;
5781         }
5782         
5783         
5784         if (this.align === 'right') {
5785             cfg.cn[0].cls += ' navbar-right';
5786         }
5787         
5788         
5789         
5790         
5791         return cfg;
5792     
5793         
5794     }
5795     
5796     
5797     
5798 });
5799
5800
5801
5802  
5803
5804  
5805        /*
5806  * - LGPL
5807  *
5808  * navbar
5809  * navbar-fixed-top
5810  * navbar-expand-md  fixed-top 
5811  */
5812
5813 /**
5814  * @class Roo.bootstrap.NavHeaderbar
5815  * @extends Roo.bootstrap.NavSimplebar
5816  * Bootstrap Sidebar class
5817  *
5818  * @cfg {String} brand what is brand
5819  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5820  * @cfg {String} brand_href href of the brand
5821  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5822  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5823  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5824  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5825  * 
5826  * @constructor
5827  * Create a new Sidebar
5828  * @param {Object} config The config object
5829  */
5830
5831
5832 Roo.bootstrap.NavHeaderbar = function(config){
5833     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5834       
5835 };
5836
5837 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5838     
5839     position: '',
5840     brand: '',
5841     brand_href: false,
5842     srButton : true,
5843     autohide : false,
5844     desktopCenter : false,
5845    
5846     
5847     getAutoCreate : function(){
5848         
5849         var   cfg = {
5850             tag: this.nav || 'nav',
5851             cls: 'navbar navbar-expand-md',
5852             role: 'navigation',
5853             cn: []
5854         };
5855         
5856         var cn = cfg.cn;
5857         if (this.desktopCenter) {
5858             cn.push({cls : 'container', cn : []});
5859             cn = cn[0].cn;
5860         }
5861         
5862         if(this.srButton){
5863             var btn = {
5864                 tag: 'button',
5865                 type: 'button',
5866                 cls: 'navbar-toggle navbar-toggler',
5867                 'data-toggle': 'collapse',
5868                 cn: [
5869                     {
5870                         tag: 'span',
5871                         cls: 'sr-only',
5872                         html: 'Toggle navigation'
5873                     },
5874                     {
5875                         tag: 'span',
5876                         cls: 'icon-bar navbar-toggler-icon'
5877                     },
5878                     {
5879                         tag: 'span',
5880                         cls: 'icon-bar'
5881                     },
5882                     {
5883                         tag: 'span',
5884                         cls: 'icon-bar'
5885                     }
5886                 ]
5887             };
5888             
5889             cn.push( Roo.bootstrap.version == 4 ? btn : {
5890                 tag: 'div',
5891                 cls: 'navbar-header',
5892                 cn: [
5893                     btn
5894                 ]
5895             });
5896         }
5897         
5898         cn.push({
5899             tag: 'div',
5900             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5901             cn : []
5902         });
5903         
5904         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5905         
5906         if (['light','white'].indexOf(this.weight) > -1) {
5907             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5908         }
5909         cfg.cls += ' bg-' + this.weight;
5910         
5911         
5912         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5913             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5914             
5915             // tag can override this..
5916             
5917             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5918         }
5919         
5920         if (this.brand !== '') {
5921             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5922             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5923                 tag: 'a',
5924                 href: this.brand_href ? this.brand_href : '#',
5925                 cls: 'navbar-brand',
5926                 cn: [
5927                 this.brand
5928                 ]
5929             });
5930         }
5931         
5932         if(this.main){
5933             cfg.cls += ' main-nav';
5934         }
5935         
5936         
5937         return cfg;
5938
5939         
5940     },
5941     getHeaderChildContainer : function()
5942     {
5943         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5944             return this.el.select('.navbar-header',true).first();
5945         }
5946         
5947         return this.getChildContainer();
5948     },
5949     
5950     getChildContainer : function()
5951     {
5952          
5953         return this.el.select('.roo-navbar-collapse',true).first();
5954          
5955         
5956     },
5957     
5958     initEvents : function()
5959     {
5960         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5961         
5962         if (this.autohide) {
5963             
5964             var prevScroll = 0;
5965             var ft = this.el;
5966             
5967             Roo.get(document).on('scroll',function(e) {
5968                 var ns = Roo.get(document).getScroll().top;
5969                 var os = prevScroll;
5970                 prevScroll = ns;
5971                 
5972                 if(ns > os){
5973                     ft.removeClass('slideDown');
5974                     ft.addClass('slideUp');
5975                     return;
5976                 }
5977                 ft.removeClass('slideUp');
5978                 ft.addClass('slideDown');
5979                  
5980               
5981           },this);
5982         }
5983     }    
5984     
5985 });
5986
5987
5988
5989  
5990
5991  /*
5992  * - LGPL
5993  *
5994  * navbar
5995  * 
5996  */
5997
5998 /**
5999  * @class Roo.bootstrap.NavSidebar
6000  * @extends Roo.bootstrap.Navbar
6001  * Bootstrap Sidebar class
6002  * 
6003  * @constructor
6004  * Create a new Sidebar
6005  * @param {Object} config The config object
6006  */
6007
6008
6009 Roo.bootstrap.NavSidebar = function(config){
6010     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6011 };
6012
6013 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6014     
6015     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6016     
6017     getAutoCreate : function(){
6018         
6019         
6020         return  {
6021             tag: 'div',
6022             cls: 'sidebar sidebar-nav'
6023         };
6024     
6025         
6026     }
6027     
6028     
6029     
6030 });
6031
6032
6033
6034  
6035
6036  /*
6037  * - LGPL
6038  *
6039  * nav group
6040  * 
6041  */
6042
6043 /**
6044  * @class Roo.bootstrap.NavGroup
6045  * @extends Roo.bootstrap.Component
6046  * Bootstrap NavGroup class
6047  * @cfg {String} align (left|right)
6048  * @cfg {Boolean} inverse
6049  * @cfg {String} type (nav|pills|tab) default nav
6050  * @cfg {String} navId - reference Id for navbar.
6051  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6052  * 
6053  * @constructor
6054  * Create a new nav group
6055  * @param {Object} config The config object
6056  */
6057
6058 Roo.bootstrap.NavGroup = function(config){
6059     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6060     this.navItems = [];
6061    
6062     Roo.bootstrap.NavGroup.register(this);
6063      this.addEvents({
6064         /**
6065              * @event changed
6066              * Fires when the active item changes
6067              * @param {Roo.bootstrap.NavGroup} this
6068              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6069              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6070          */
6071         'changed': true
6072      });
6073     
6074 };
6075
6076 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6077     
6078     align: '',
6079     inverse: false,
6080     form: false,
6081     type: 'nav',
6082     navId : '',
6083     // private
6084     pilltype : true,
6085     
6086     navItems : false, 
6087     
6088     getAutoCreate : function()
6089     {
6090         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6091         
6092         cfg = {
6093             tag : 'ul',
6094             cls: 'nav' 
6095         };
6096         if (Roo.bootstrap.version == 4) {
6097             if (['tabs','pills'].indexOf(this.type) != -1) {
6098                 cfg.cls += ' nav-' + this.type; 
6099             } else {
6100                 // trying to remove so header bar can right align top?
6101                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6102                     // do not use on header bar... 
6103                     cfg.cls += ' navbar-nav';
6104                 }
6105             }
6106             
6107         } else {
6108             if (['tabs','pills'].indexOf(this.type) != -1) {
6109                 cfg.cls += ' nav-' + this.type
6110             } else {
6111                 if (this.type !== 'nav') {
6112                     Roo.log('nav type must be nav/tabs/pills')
6113                 }
6114                 cfg.cls += ' navbar-nav'
6115             }
6116         }
6117         
6118         if (this.parent() && this.parent().sidebar) {
6119             cfg = {
6120                 tag: 'ul',
6121                 cls: 'dashboard-menu sidebar-menu'
6122             };
6123             
6124             return cfg;
6125         }
6126         
6127         if (this.form === true) {
6128             cfg = {
6129                 tag: 'form',
6130                 cls: 'navbar-form form-inline'
6131             };
6132             //nav navbar-right ml-md-auto
6133             if (this.align === 'right') {
6134                 cfg.cls += ' navbar-right ml-md-auto';
6135             } else {
6136                 cfg.cls += ' navbar-left';
6137             }
6138         }
6139         
6140         if (this.align === 'right') {
6141             cfg.cls += ' navbar-right ml-md-auto';
6142         } else {
6143             cfg.cls += ' mr-auto';
6144         }
6145         
6146         if (this.inverse) {
6147             cfg.cls += ' navbar-inverse';
6148             
6149         }
6150         
6151         
6152         return cfg;
6153     },
6154     /**
6155     * sets the active Navigation item
6156     * @param {Roo.bootstrap.NavItem} the new current navitem
6157     */
6158     setActiveItem : function(item)
6159     {
6160         var prev = false;
6161         Roo.each(this.navItems, function(v){
6162             if (v == item) {
6163                 return ;
6164             }
6165             if (v.isActive()) {
6166                 v.setActive(false, true);
6167                 prev = v;
6168                 
6169             }
6170             
6171         });
6172
6173         item.setActive(true, true);
6174         this.fireEvent('changed', this, item, prev);
6175         
6176         
6177     },
6178     /**
6179     * gets the active Navigation item
6180     * @return {Roo.bootstrap.NavItem} the current navitem
6181     */
6182     getActive : function()
6183     {
6184         
6185         var prev = false;
6186         Roo.each(this.navItems, function(v){
6187             
6188             if (v.isActive()) {
6189                 prev = v;
6190                 
6191             }
6192             
6193         });
6194         return prev;
6195     },
6196     
6197     indexOfNav : function()
6198     {
6199         
6200         var prev = false;
6201         Roo.each(this.navItems, function(v,i){
6202             
6203             if (v.isActive()) {
6204                 prev = i;
6205                 
6206             }
6207             
6208         });
6209         return prev;
6210     },
6211     /**
6212     * adds a Navigation item
6213     * @param {Roo.bootstrap.NavItem} the navitem to add
6214     */
6215     addItem : function(cfg)
6216     {
6217         if (this.form && Roo.bootstrap.version == 4) {
6218             cfg.tag = 'div';
6219         }
6220         var cn = new Roo.bootstrap.NavItem(cfg);
6221         this.register(cn);
6222         cn.parentId = this.id;
6223         cn.onRender(this.el, null);
6224         return cn;
6225     },
6226     /**
6227     * register a Navigation item
6228     * @param {Roo.bootstrap.NavItem} the navitem to add
6229     */
6230     register : function(item)
6231     {
6232         this.navItems.push( item);
6233         item.navId = this.navId;
6234     
6235     },
6236     
6237     /**
6238     * clear all the Navigation item
6239     */
6240    
6241     clearAll : function()
6242     {
6243         this.navItems = [];
6244         this.el.dom.innerHTML = '';
6245     },
6246     
6247     getNavItem: function(tabId)
6248     {
6249         var ret = false;
6250         Roo.each(this.navItems, function(e) {
6251             if (e.tabId == tabId) {
6252                ret =  e;
6253                return false;
6254             }
6255             return true;
6256             
6257         });
6258         return ret;
6259     },
6260     
6261     setActiveNext : function()
6262     {
6263         var i = this.indexOfNav(this.getActive());
6264         if (i > this.navItems.length) {
6265             return;
6266         }
6267         this.setActiveItem(this.navItems[i+1]);
6268     },
6269     setActivePrev : function()
6270     {
6271         var i = this.indexOfNav(this.getActive());
6272         if (i  < 1) {
6273             return;
6274         }
6275         this.setActiveItem(this.navItems[i-1]);
6276     },
6277     clearWasActive : function(except) {
6278         Roo.each(this.navItems, function(e) {
6279             if (e.tabId != except.tabId && e.was_active) {
6280                e.was_active = false;
6281                return false;
6282             }
6283             return true;
6284             
6285         });
6286     },
6287     getWasActive : function ()
6288     {
6289         var r = false;
6290         Roo.each(this.navItems, function(e) {
6291             if (e.was_active) {
6292                r = e;
6293                return false;
6294             }
6295             return true;
6296             
6297         });
6298         return r;
6299     }
6300     
6301     
6302 });
6303
6304  
6305 Roo.apply(Roo.bootstrap.NavGroup, {
6306     
6307     groups: {},
6308      /**
6309     * register a Navigation Group
6310     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6311     */
6312     register : function(navgrp)
6313     {
6314         this.groups[navgrp.navId] = navgrp;
6315         
6316     },
6317     /**
6318     * fetch a Navigation Group based on the navigation ID
6319     * @param {string} the navgroup to add
6320     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6321     */
6322     get: function(navId) {
6323         if (typeof(this.groups[navId]) == 'undefined') {
6324             return false;
6325             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6326         }
6327         return this.groups[navId] ;
6328     }
6329     
6330     
6331     
6332 });
6333
6334  /*
6335  * - LGPL
6336  *
6337  * row
6338  * 
6339  */
6340
6341 /**
6342  * @class Roo.bootstrap.NavItem
6343  * @extends Roo.bootstrap.Component
6344  * Bootstrap Navbar.NavItem class
6345  * @cfg {String} href  link to
6346  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6347  * @cfg {Boolean} button_outline show and outlined button
6348  * @cfg {String} html content of button
6349  * @cfg {String} badge text inside badge
6350  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6351  * @cfg {String} glyphicon DEPRICATED - use fa
6352  * @cfg {String} icon DEPRICATED - use fa
6353  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6354  * @cfg {Boolean} active Is item active
6355  * @cfg {Boolean} disabled Is item disabled
6356  * @cfg {String} linkcls  Link Class
6357  * @cfg {Boolean} preventDefault (true | false) default false
6358  * @cfg {String} tabId the tab that this item activates.
6359  * @cfg {String} tagtype (a|span) render as a href or span?
6360  * @cfg {Boolean} animateRef (true|false) link to element default false  
6361   
6362  * @constructor
6363  * Create a new Navbar Item
6364  * @param {Object} config The config object
6365  */
6366 Roo.bootstrap.NavItem = function(config){
6367     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6368     this.addEvents({
6369         // raw events
6370         /**
6371          * @event click
6372          * The raw click event for the entire grid.
6373          * @param {Roo.EventObject} e
6374          */
6375         "click" : true,
6376          /**
6377             * @event changed
6378             * Fires when the active item active state changes
6379             * @param {Roo.bootstrap.NavItem} this
6380             * @param {boolean} state the new state
6381              
6382          */
6383         'changed': true,
6384         /**
6385             * @event scrollto
6386             * Fires when scroll to element
6387             * @param {Roo.bootstrap.NavItem} this
6388             * @param {Object} options
6389             * @param {Roo.EventObject} e
6390              
6391          */
6392         'scrollto': true
6393     });
6394    
6395 };
6396
6397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6398     
6399     href: false,
6400     html: '',
6401     badge: '',
6402     icon: false,
6403     fa : false,
6404     glyphicon: false,
6405     active: false,
6406     preventDefault : false,
6407     tabId : false,
6408     tagtype : 'a',
6409     tag: 'li',
6410     disabled : false,
6411     animateRef : false,
6412     was_active : false,
6413     button_weight : '',
6414     button_outline : false,
6415     linkcls : '',
6416     navLink: false,
6417     
6418     getAutoCreate : function(){
6419          
6420         var cfg = {
6421             tag: this.tag,
6422             cls: 'nav-item'
6423         };
6424         
6425         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6426         
6427         if (this.active) {
6428             cfg.cls +=  ' active' ;
6429         }
6430         if (this.disabled) {
6431             cfg.cls += ' disabled';
6432         }
6433         
6434         // BS4 only?
6435         if (this.button_weight.length) {
6436             cfg.tag = this.href ? 'a' : 'button';
6437             cfg.html = this.html || '';
6438             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6439             if (this.href) {
6440                 cfg.href = this.href;
6441             }
6442             if (this.fa) {
6443                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6444             } else {
6445                 cfg.cls += " nav-html";
6446             }
6447             
6448             // menu .. should add dropdown-menu class - so no need for carat..
6449             
6450             if (this.badge !== '') {
6451                  
6452                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6453             }
6454             return cfg;
6455         }
6456         
6457         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6458             cfg.cn = [
6459                 {
6460                     tag: this.tagtype,
6461                     href : this.href || "#",
6462                     html: this.html || '',
6463                     cls : ''
6464                 }
6465             ];
6466             if (this.tagtype == 'a') {
6467                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6468         
6469             }
6470             if (this.icon) {
6471                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6472             } else  if (this.fa) {
6473                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6474             } else if(this.glyphicon) {
6475                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6476             } else {
6477                 cfg.cn[0].cls += " nav-html";
6478             }
6479             
6480             if (this.menu) {
6481                 cfg.cn[0].html += " <span class='caret'></span>";
6482              
6483             }
6484             
6485             if (this.badge !== '') {
6486                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6487             }
6488         }
6489         
6490         
6491         
6492         return cfg;
6493     },
6494     onRender : function(ct, position)
6495     {
6496        // Roo.log("Call onRender: " + this.xtype);
6497         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6498             this.tag = 'div';
6499         }
6500         
6501         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6502         this.navLink = this.el.select('.nav-link',true).first();
6503         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6504         return ret;
6505     },
6506       
6507     
6508     initEvents: function() 
6509     {
6510         if (typeof (this.menu) != 'undefined') {
6511             this.menu.parentType = this.xtype;
6512             this.menu.triggerEl = this.el;
6513             this.menu = this.addxtype(Roo.apply({}, this.menu));
6514         }
6515         
6516         this.el.on('click', this.onClick, this);
6517         
6518         //if(this.tagtype == 'span'){
6519         //    this.el.select('span',true).on('click', this.onClick, this);
6520         //}
6521        
6522         // at this point parent should be available..
6523         this.parent().register(this);
6524     },
6525     
6526     onClick : function(e)
6527     {
6528         if (e.getTarget('.dropdown-menu-item')) {
6529             // did you click on a menu itemm.... - then don't trigger onclick..
6530             return;
6531         }
6532         
6533         if(
6534                 this.preventDefault || 
6535                 this.href == '#' 
6536         ){
6537             Roo.log("NavItem - prevent Default?");
6538             e.preventDefault();
6539         }
6540         
6541         if (this.disabled) {
6542             return;
6543         }
6544         
6545         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6546         if (tg && tg.transition) {
6547             Roo.log("waiting for the transitionend");
6548             return;
6549         }
6550         
6551         
6552         
6553         //Roo.log("fire event clicked");
6554         if(this.fireEvent('click', this, e) === false){
6555             return;
6556         };
6557         
6558         if(this.tagtype == 'span'){
6559             return;
6560         }
6561         
6562         //Roo.log(this.href);
6563         var ael = this.el.select('a',true).first();
6564         //Roo.log(ael);
6565         
6566         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6567             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6568             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6569                 return; // ignore... - it's a 'hash' to another page.
6570             }
6571             Roo.log("NavItem - prevent Default?");
6572             e.preventDefault();
6573             this.scrollToElement(e);
6574         }
6575         
6576         
6577         var p =  this.parent();
6578    
6579         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6580             if (typeof(p.setActiveItem) !== 'undefined') {
6581                 p.setActiveItem(this);
6582             }
6583         }
6584         
6585         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6586         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6587             // remove the collapsed menu expand...
6588             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6589         }
6590     },
6591     
6592     isActive: function () {
6593         return this.active
6594     },
6595     setActive : function(state, fire, is_was_active)
6596     {
6597         if (this.active && !state && this.navId) {
6598             this.was_active = true;
6599             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6600             if (nv) {
6601                 nv.clearWasActive(this);
6602             }
6603             
6604         }
6605         this.active = state;
6606         
6607         if (!state ) {
6608             this.el.removeClass('active');
6609             this.navLink ? this.navLink.removeClass('active') : false;
6610         } else if (!this.el.hasClass('active')) {
6611             
6612             this.el.addClass('active');
6613             if (Roo.bootstrap.version == 4 && this.navLink ) {
6614                 this.navLink.addClass('active');
6615             }
6616             
6617         }
6618         if (fire) {
6619             this.fireEvent('changed', this, state);
6620         }
6621         
6622         // show a panel if it's registered and related..
6623         
6624         if (!this.navId || !this.tabId || !state || is_was_active) {
6625             return;
6626         }
6627         
6628         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6629         if (!tg) {
6630             return;
6631         }
6632         var pan = tg.getPanelByName(this.tabId);
6633         if (!pan) {
6634             return;
6635         }
6636         // if we can not flip to new panel - go back to old nav highlight..
6637         if (false == tg.showPanel(pan)) {
6638             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6639             if (nv) {
6640                 var onav = nv.getWasActive();
6641                 if (onav) {
6642                     onav.setActive(true, false, true);
6643                 }
6644             }
6645             
6646         }
6647         
6648         
6649         
6650     },
6651      // this should not be here...
6652     setDisabled : function(state)
6653     {
6654         this.disabled = state;
6655         if (!state ) {
6656             this.el.removeClass('disabled');
6657         } else if (!this.el.hasClass('disabled')) {
6658             this.el.addClass('disabled');
6659         }
6660         
6661     },
6662     
6663     /**
6664      * Fetch the element to display the tooltip on.
6665      * @return {Roo.Element} defaults to this.el
6666      */
6667     tooltipEl : function()
6668     {
6669         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6670     },
6671     
6672     scrollToElement : function(e)
6673     {
6674         var c = document.body;
6675         
6676         /*
6677          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6678          */
6679         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6680             c = document.documentElement;
6681         }
6682         
6683         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6684         
6685         if(!target){
6686             return;
6687         }
6688
6689         var o = target.calcOffsetsTo(c);
6690         
6691         var options = {
6692             target : target,
6693             value : o[1]
6694         };
6695         
6696         this.fireEvent('scrollto', this, options, e);
6697         
6698         Roo.get(c).scrollTo('top', options.value, true);
6699         
6700         return;
6701     },
6702     /**
6703      * Set the HTML (text content) of the item
6704      * @param {string} html  content for the nav item
6705      */
6706     setHtml : function(html)
6707     {
6708         this.html = html;
6709         this.htmlEl.dom.innerHTML = html;
6710         
6711     } 
6712 });
6713  
6714
6715  /*
6716  * - LGPL
6717  *
6718  * sidebar item
6719  *
6720  *  li
6721  *    <span> icon </span>
6722  *    <span> text </span>
6723  *    <span>badge </span>
6724  */
6725
6726 /**
6727  * @class Roo.bootstrap.NavSidebarItem
6728  * @extends Roo.bootstrap.NavItem
6729  * Bootstrap Navbar.NavSidebarItem class
6730  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6731  * {Boolean} open is the menu open
6732  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6733  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6734  * {String} buttonSize (sm|md|lg)the extra classes for the button
6735  * {Boolean} showArrow show arrow next to the text (default true)
6736  * @constructor
6737  * Create a new Navbar Button
6738  * @param {Object} config The config object
6739  */
6740 Roo.bootstrap.NavSidebarItem = function(config){
6741     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6742     this.addEvents({
6743         // raw events
6744         /**
6745          * @event click
6746          * The raw click event for the entire grid.
6747          * @param {Roo.EventObject} e
6748          */
6749         "click" : true,
6750          /**
6751             * @event changed
6752             * Fires when the active item active state changes
6753             * @param {Roo.bootstrap.NavSidebarItem} this
6754             * @param {boolean} state the new state
6755              
6756          */
6757         'changed': true
6758     });
6759    
6760 };
6761
6762 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6763     
6764     badgeWeight : 'default',
6765     
6766     open: false,
6767     
6768     buttonView : false,
6769     
6770     buttonWeight : 'default',
6771     
6772     buttonSize : 'md',
6773     
6774     showArrow : true,
6775     
6776     getAutoCreate : function(){
6777         
6778         
6779         var a = {
6780                 tag: 'a',
6781                 href : this.href || '#',
6782                 cls: '',
6783                 html : '',
6784                 cn : []
6785         };
6786         
6787         if(this.buttonView){
6788             a = {
6789                 tag: 'button',
6790                 href : this.href || '#',
6791                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6792                 html : this.html,
6793                 cn : []
6794             };
6795         }
6796         
6797         var cfg = {
6798             tag: 'li',
6799             cls: '',
6800             cn: [ a ]
6801         };
6802         
6803         if (this.active) {
6804             cfg.cls += ' active';
6805         }
6806         
6807         if (this.disabled) {
6808             cfg.cls += ' disabled';
6809         }
6810         if (this.open) {
6811             cfg.cls += ' open x-open';
6812         }
6813         // left icon..
6814         if (this.glyphicon || this.icon) {
6815             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6816             a.cn.push({ tag : 'i', cls : c }) ;
6817         }
6818         
6819         if(!this.buttonView){
6820             var span = {
6821                 tag: 'span',
6822                 html : this.html || ''
6823             };
6824
6825             a.cn.push(span);
6826             
6827         }
6828         
6829         if (this.badge !== '') {
6830             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6831         }
6832         
6833         if (this.menu) {
6834             
6835             if(this.showArrow){
6836                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6837             }
6838             
6839             a.cls += ' dropdown-toggle treeview' ;
6840         }
6841         
6842         return cfg;
6843     },
6844     
6845     initEvents : function()
6846     { 
6847         if (typeof (this.menu) != 'undefined') {
6848             this.menu.parentType = this.xtype;
6849             this.menu.triggerEl = this.el;
6850             this.menu = this.addxtype(Roo.apply({}, this.menu));
6851         }
6852         
6853         this.el.on('click', this.onClick, this);
6854         
6855         if(this.badge !== ''){
6856             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6857         }
6858         
6859     },
6860     
6861     onClick : function(e)
6862     {
6863         if(this.disabled){
6864             e.preventDefault();
6865             return;
6866         }
6867         
6868         if(this.preventDefault){
6869             e.preventDefault();
6870         }
6871         
6872         this.fireEvent('click', this, e);
6873     },
6874     
6875     disable : function()
6876     {
6877         this.setDisabled(true);
6878     },
6879     
6880     enable : function()
6881     {
6882         this.setDisabled(false);
6883     },
6884     
6885     setDisabled : function(state)
6886     {
6887         if(this.disabled == state){
6888             return;
6889         }
6890         
6891         this.disabled = state;
6892         
6893         if (state) {
6894             this.el.addClass('disabled');
6895             return;
6896         }
6897         
6898         this.el.removeClass('disabled');
6899         
6900         return;
6901     },
6902     
6903     setActive : function(state)
6904     {
6905         if(this.active == state){
6906             return;
6907         }
6908         
6909         this.active = state;
6910         
6911         if (state) {
6912             this.el.addClass('active');
6913             return;
6914         }
6915         
6916         this.el.removeClass('active');
6917         
6918         return;
6919     },
6920     
6921     isActive: function () 
6922     {
6923         return this.active;
6924     },
6925     
6926     setBadge : function(str)
6927     {
6928         if(!this.badgeEl){
6929             return;
6930         }
6931         
6932         this.badgeEl.dom.innerHTML = str;
6933     }
6934     
6935    
6936      
6937  
6938 });
6939  
6940
6941  /*
6942  * - LGPL
6943  *
6944  *  Breadcrumb Nav
6945  * 
6946  */
6947 Roo.namespace('Roo.bootstrap.breadcrumb');
6948
6949
6950 /**
6951  * @class Roo.bootstrap.breadcrumb.Nav
6952  * @extends Roo.bootstrap.Component
6953  * Bootstrap Breadcrumb Nav Class
6954  *  
6955  * @children Roo.bootstrap.breadcrumb.Item
6956  * 
6957  * @constructor
6958  * Create a new breadcrumb.Nav
6959  * @param {Object} config The config object
6960  */
6961
6962
6963 Roo.bootstrap.breadcrumb.Nav = function(config){
6964     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6965     
6966     
6967 };
6968
6969 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6970     
6971     getAutoCreate : function()
6972     {
6973
6974         var cfg = {
6975             tag: 'nav',
6976             cn : [
6977                 {
6978                     tag : 'ol',
6979                     cls : 'breadcrumb'
6980                 }
6981             ]
6982             
6983         };
6984           
6985         return cfg;
6986     },
6987     
6988     initEvents: function()
6989     {
6990         this.olEl = this.el.select('ol',true).first();    
6991     },
6992     getChildContainer : function()
6993     {
6994         return this.olEl;  
6995     }
6996     
6997 });
6998
6999  /*
7000  * - LGPL
7001  *
7002  *  Breadcrumb Item
7003  * 
7004  */
7005
7006
7007 /**
7008  * @class Roo.bootstrap.breadcrumb.Nav
7009  * @extends Roo.bootstrap.Component
7010  * Bootstrap Breadcrumb Nav Class
7011  *  
7012  * @children Roo.bootstrap.breadcrumb.Component
7013  * @cfg {String} html the content of the link.
7014  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7015  * @cfg {Boolean} active is it active
7016
7017  * 
7018  * @constructor
7019  * Create a new breadcrumb.Nav
7020  * @param {Object} config The config object
7021  */
7022
7023 Roo.bootstrap.breadcrumb.Item = function(config){
7024     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7025     this.addEvents({
7026         // img events
7027         /**
7028          * @event click
7029          * The img click event for the img.
7030          * @param {Roo.EventObject} e
7031          */
7032         "click" : true
7033     });
7034     
7035 };
7036
7037 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7038     
7039     href: false,
7040     html : '',
7041     
7042     getAutoCreate : function()
7043     {
7044
7045         var cfg = {
7046             tag: 'li',
7047             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7048         };
7049         if (this.href !== false) {
7050             cfg.cn = [{
7051                 tag : 'a',
7052                 href : this.href,
7053                 html : this.html
7054             }];
7055         } else {
7056             cfg.html = this.html;
7057         }
7058         
7059         return cfg;
7060     },
7061     
7062     initEvents: function()
7063     {
7064         if (this.href) {
7065             this.el.select('a', true).first().on('click',this.onClick, this)
7066         }
7067         
7068     },
7069     onClick : function(e)
7070     {
7071         e.preventDefault();
7072         this.fireEvent('click',this,  e);
7073     }
7074     
7075 });
7076
7077  /*
7078  * - LGPL
7079  *
7080  * row
7081  * 
7082  */
7083
7084 /**
7085  * @class Roo.bootstrap.Row
7086  * @extends Roo.bootstrap.Component
7087  * Bootstrap Row class (contains columns...)
7088  * 
7089  * @constructor
7090  * Create a new Row
7091  * @param {Object} config The config object
7092  */
7093
7094 Roo.bootstrap.Row = function(config){
7095     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7096 };
7097
7098 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7099     
7100     getAutoCreate : function(){
7101        return {
7102             cls: 'row clearfix'
7103        };
7104     }
7105     
7106     
7107 });
7108
7109  
7110
7111  /*
7112  * - LGPL
7113  *
7114  * pagination
7115  * 
7116  */
7117
7118 /**
7119  * @class Roo.bootstrap.Pagination
7120  * @extends Roo.bootstrap.Component
7121  * Bootstrap Pagination class
7122  * @cfg {String} size xs | sm | md | lg
7123  * @cfg {Boolean} inverse false | true
7124  * 
7125  * @constructor
7126  * Create a new Pagination
7127  * @param {Object} config The config object
7128  */
7129
7130 Roo.bootstrap.Pagination = function(config){
7131     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7132 };
7133
7134 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7135     
7136     cls: false,
7137     size: false,
7138     inverse: false,
7139     
7140     getAutoCreate : function(){
7141         var cfg = {
7142             tag: 'ul',
7143                 cls: 'pagination'
7144         };
7145         if (this.inverse) {
7146             cfg.cls += ' inverse';
7147         }
7148         if (this.html) {
7149             cfg.html=this.html;
7150         }
7151         if (this.cls) {
7152             cfg.cls += " " + this.cls;
7153         }
7154         return cfg;
7155     }
7156    
7157 });
7158
7159  
7160
7161  /*
7162  * - LGPL
7163  *
7164  * Pagination item
7165  * 
7166  */
7167
7168
7169 /**
7170  * @class Roo.bootstrap.PaginationItem
7171  * @extends Roo.bootstrap.Component
7172  * Bootstrap PaginationItem class
7173  * @cfg {String} html text
7174  * @cfg {String} href the link
7175  * @cfg {Boolean} preventDefault (true | false) default true
7176  * @cfg {Boolean} active (true | false) default false
7177  * @cfg {Boolean} disabled default false
7178  * 
7179  * 
7180  * @constructor
7181  * Create a new PaginationItem
7182  * @param {Object} config The config object
7183  */
7184
7185
7186 Roo.bootstrap.PaginationItem = function(config){
7187     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7188     this.addEvents({
7189         // raw events
7190         /**
7191          * @event click
7192          * The raw click event for the entire grid.
7193          * @param {Roo.EventObject} e
7194          */
7195         "click" : true
7196     });
7197 };
7198
7199 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7200     
7201     href : false,
7202     html : false,
7203     preventDefault: true,
7204     active : false,
7205     cls : false,
7206     disabled: false,
7207     
7208     getAutoCreate : function(){
7209         var cfg= {
7210             tag: 'li',
7211             cn: [
7212                 {
7213                     tag : 'a',
7214                     href : this.href ? this.href : '#',
7215                     html : this.html ? this.html : ''
7216                 }
7217             ]
7218         };
7219         
7220         if(this.cls){
7221             cfg.cls = this.cls;
7222         }
7223         
7224         if(this.disabled){
7225             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7226         }
7227         
7228         if(this.active){
7229             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7230         }
7231         
7232         return cfg;
7233     },
7234     
7235     initEvents: function() {
7236         
7237         this.el.on('click', this.onClick, this);
7238         
7239     },
7240     onClick : function(e)
7241     {
7242         Roo.log('PaginationItem on click ');
7243         if(this.preventDefault){
7244             e.preventDefault();
7245         }
7246         
7247         if(this.disabled){
7248             return;
7249         }
7250         
7251         this.fireEvent('click', this, e);
7252     }
7253    
7254 });
7255
7256  
7257
7258  /*
7259  * - LGPL
7260  *
7261  * slider
7262  * 
7263  */
7264
7265
7266 /**
7267  * @class Roo.bootstrap.Slider
7268  * @extends Roo.bootstrap.Component
7269  * Bootstrap Slider class
7270  *    
7271  * @constructor
7272  * Create a new Slider
7273  * @param {Object} config The config object
7274  */
7275
7276 Roo.bootstrap.Slider = function(config){
7277     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7278 };
7279
7280 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7281     
7282     getAutoCreate : function(){
7283         
7284         var cfg = {
7285             tag: 'div',
7286             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7287             cn: [
7288                 {
7289                     tag: 'a',
7290                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7291                 }
7292             ]
7293         };
7294         
7295         return cfg;
7296     }
7297    
7298 });
7299
7300  /*
7301  * Based on:
7302  * Ext JS Library 1.1.1
7303  * Copyright(c) 2006-2007, Ext JS, LLC.
7304  *
7305  * Originally Released Under LGPL - original licence link has changed is not relivant.
7306  *
7307  * Fork - LGPL
7308  * <script type="text/javascript">
7309  */
7310  /**
7311  * @extends Roo.dd.DDProxy
7312  * @class Roo.grid.SplitDragZone
7313  * Support for Column Header resizing
7314  * @constructor
7315  * @param {Object} config
7316  */
7317 // private
7318 // This is a support class used internally by the Grid components
7319 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7320     this.grid = grid;
7321     this.view = grid.getView();
7322     this.proxy = this.view.resizeProxy;
7323     Roo.grid.SplitDragZone.superclass.constructor.call(
7324         this,
7325         hd, // ID
7326         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7327         {  // CONFIG
7328             dragElId : Roo.id(this.proxy.dom),
7329             resizeFrame:false
7330         }
7331     );
7332     
7333     this.setHandleElId(Roo.id(hd));
7334     if (hd2 !== false) {
7335         this.setOuterHandleElId(Roo.id(hd2));
7336     }
7337     
7338     this.scroll = false;
7339 };
7340 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7341     fly: Roo.Element.fly,
7342
7343     b4StartDrag : function(x, y){
7344         this.view.headersDisabled = true;
7345         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7346                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7347         );
7348         this.proxy.setHeight(h);
7349         
7350         // for old system colWidth really stored the actual width?
7351         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7352         // which in reality did not work.. - it worked only for fixed sizes
7353         // for resizable we need to use actual sizes.
7354         var w = this.cm.getColumnWidth(this.cellIndex);
7355         if (!this.view.mainWrap) {
7356             // bootstrap.
7357             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7358         }
7359         
7360         
7361         
7362         // this was w-this.grid.minColumnWidth;
7363         // doesnt really make sense? - w = thie curren width or the rendered one?
7364         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7365         this.resetConstraints();
7366         this.setXConstraint(minw, 1000);
7367         this.setYConstraint(0, 0);
7368         this.minX = x - minw;
7369         this.maxX = x + 1000;
7370         this.startPos = x;
7371         if (!this.view.mainWrap) { // this is Bootstrap code..
7372             this.getDragEl().style.display='block';
7373         }
7374         
7375         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7376     },
7377
7378
7379     handleMouseDown : function(e){
7380         ev = Roo.EventObject.setEvent(e);
7381         var t = this.fly(ev.getTarget());
7382         if(t.hasClass("x-grid-split")){
7383             this.cellIndex = this.view.getCellIndex(t.dom);
7384             this.split = t.dom;
7385             this.cm = this.grid.colModel;
7386             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7387                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7388             }
7389         }
7390     },
7391
7392     endDrag : function(e){
7393         this.view.headersDisabled = false;
7394         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7395         var diff = endX - this.startPos;
7396         // 
7397         var w = this.cm.getColumnWidth(this.cellIndex);
7398         if (!this.view.mainWrap) {
7399             w = 0;
7400         }
7401         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7402     },
7403
7404     autoOffset : function(){
7405         this.setDelta(0,0);
7406     }
7407 });/*
7408  * Based on:
7409  * Ext JS Library 1.1.1
7410  * Copyright(c) 2006-2007, Ext JS, LLC.
7411  *
7412  * Originally Released Under LGPL - original licence link has changed is not relivant.
7413  *
7414  * Fork - LGPL
7415  * <script type="text/javascript">
7416  */
7417
7418 /**
7419  * @class Roo.grid.AbstractSelectionModel
7420  * @extends Roo.util.Observable
7421  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7422  * implemented by descendant classes.  This class should not be directly instantiated.
7423  * @constructor
7424  */
7425 Roo.grid.AbstractSelectionModel = function(){
7426     this.locked = false;
7427     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7428 };
7429
7430 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7431     /** @ignore Called by the grid automatically. Do not call directly. */
7432     init : function(grid){
7433         this.grid = grid;
7434         this.initEvents();
7435     },
7436
7437     /**
7438      * Locks the selections.
7439      */
7440     lock : function(){
7441         this.locked = true;
7442     },
7443
7444     /**
7445      * Unlocks the selections.
7446      */
7447     unlock : function(){
7448         this.locked = false;
7449     },
7450
7451     /**
7452      * Returns true if the selections are locked.
7453      * @return {Boolean}
7454      */
7455     isLocked : function(){
7456         return this.locked;
7457     }
7458 });/*
7459  * Based on:
7460  * Ext JS Library 1.1.1
7461  * Copyright(c) 2006-2007, Ext JS, LLC.
7462  *
7463  * Originally Released Under LGPL - original licence link has changed is not relivant.
7464  *
7465  * Fork - LGPL
7466  * <script type="text/javascript">
7467  */
7468 /**
7469  * @extends Roo.grid.AbstractSelectionModel
7470  * @class Roo.grid.RowSelectionModel
7471  * The default SelectionModel used by {@link Roo.grid.Grid}.
7472  * It supports multiple selections and keyboard selection/navigation. 
7473  * @constructor
7474  * @param {Object} config
7475  */
7476 Roo.grid.RowSelectionModel = function(config){
7477     Roo.apply(this, config);
7478     this.selections = new Roo.util.MixedCollection(false, function(o){
7479         return o.id;
7480     });
7481
7482     this.last = false;
7483     this.lastActive = false;
7484
7485     this.addEvents({
7486         /**
7487         * @event selectionchange
7488         * Fires when the selection changes
7489         * @param {SelectionModel} this
7490         */
7491        "selectionchange" : true,
7492        /**
7493         * @event afterselectionchange
7494         * Fires after the selection changes (eg. by key press or clicking)
7495         * @param {SelectionModel} this
7496         */
7497        "afterselectionchange" : true,
7498        /**
7499         * @event beforerowselect
7500         * Fires when a row is selected being selected, return false to cancel.
7501         * @param {SelectionModel} this
7502         * @param {Number} rowIndex The selected index
7503         * @param {Boolean} keepExisting False if other selections will be cleared
7504         */
7505        "beforerowselect" : true,
7506        /**
7507         * @event rowselect
7508         * Fires when a row is selected.
7509         * @param {SelectionModel} this
7510         * @param {Number} rowIndex The selected index
7511         * @param {Roo.data.Record} r The record
7512         */
7513        "rowselect" : true,
7514        /**
7515         * @event rowdeselect
7516         * Fires when a row is deselected.
7517         * @param {SelectionModel} this
7518         * @param {Number} rowIndex The selected index
7519         */
7520         "rowdeselect" : true
7521     });
7522     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7523     this.locked = false;
7524 };
7525
7526 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7527     /**
7528      * @cfg {Boolean} singleSelect
7529      * True to allow selection of only one row at a time (defaults to false)
7530      */
7531     singleSelect : false,
7532
7533     // private
7534     initEvents : function(){
7535
7536         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7537             this.grid.on("mousedown", this.handleMouseDown, this);
7538         }else{ // allow click to work like normal
7539             this.grid.on("rowclick", this.handleDragableRowClick, this);
7540         }
7541         // bootstrap does not have a view..
7542         var view = this.grid.view ? this.grid.view : this.grid;
7543         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7544             "up" : function(e){
7545                 if(!e.shiftKey){
7546                     this.selectPrevious(e.shiftKey);
7547                 }else if(this.last !== false && this.lastActive !== false){
7548                     var last = this.last;
7549                     this.selectRange(this.last,  this.lastActive-1);
7550                     view.focusRow(this.lastActive);
7551                     if(last !== false){
7552                         this.last = last;
7553                     }
7554                 }else{
7555                     this.selectFirstRow();
7556                 }
7557                 this.fireEvent("afterselectionchange", this);
7558             },
7559             "down" : function(e){
7560                 if(!e.shiftKey){
7561                     this.selectNext(e.shiftKey);
7562                 }else if(this.last !== false && this.lastActive !== false){
7563                     var last = this.last;
7564                     this.selectRange(this.last,  this.lastActive+1);
7565                     view.focusRow(this.lastActive);
7566                     if(last !== false){
7567                         this.last = last;
7568                     }
7569                 }else{
7570                     this.selectFirstRow();
7571                 }
7572                 this.fireEvent("afterselectionchange", this);
7573             },
7574             scope: this
7575         });
7576
7577          
7578         view.on("refresh", this.onRefresh, this);
7579         view.on("rowupdated", this.onRowUpdated, this);
7580         view.on("rowremoved", this.onRemove, this);
7581     },
7582
7583     // private
7584     onRefresh : function(){
7585         var ds = this.grid.ds, i, v = this.grid.view;
7586         var s = this.selections;
7587         s.each(function(r){
7588             if((i = ds.indexOfId(r.id)) != -1){
7589                 v.onRowSelect(i);
7590                 s.add(ds.getAt(i)); // updating the selection relate data
7591             }else{
7592                 s.remove(r);
7593             }
7594         });
7595     },
7596
7597     // private
7598     onRemove : function(v, index, r){
7599         this.selections.remove(r);
7600     },
7601
7602     // private
7603     onRowUpdated : function(v, index, r){
7604         if(this.isSelected(r)){
7605             v.onRowSelect(index);
7606         }
7607     },
7608
7609     /**
7610      * Select records.
7611      * @param {Array} records The records to select
7612      * @param {Boolean} keepExisting (optional) True to keep existing selections
7613      */
7614     selectRecords : function(records, keepExisting){
7615         if(!keepExisting){
7616             this.clearSelections();
7617         }
7618         var ds = this.grid.ds;
7619         for(var i = 0, len = records.length; i < len; i++){
7620             this.selectRow(ds.indexOf(records[i]), true);
7621         }
7622     },
7623
7624     /**
7625      * Gets the number of selected rows.
7626      * @return {Number}
7627      */
7628     getCount : function(){
7629         return this.selections.length;
7630     },
7631
7632     /**
7633      * Selects the first row in the grid.
7634      */
7635     selectFirstRow : function(){
7636         this.selectRow(0);
7637     },
7638
7639     /**
7640      * Select the last row.
7641      * @param {Boolean} keepExisting (optional) True to keep existing selections
7642      */
7643     selectLastRow : function(keepExisting){
7644         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7645     },
7646
7647     /**
7648      * Selects the row immediately following the last selected row.
7649      * @param {Boolean} keepExisting (optional) True to keep existing selections
7650      */
7651     selectNext : function(keepExisting){
7652         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7653             this.selectRow(this.last+1, keepExisting);
7654             var view = this.grid.view ? this.grid.view : this.grid;
7655             view.focusRow(this.last);
7656         }
7657     },
7658
7659     /**
7660      * Selects the row that precedes the last selected row.
7661      * @param {Boolean} keepExisting (optional) True to keep existing selections
7662      */
7663     selectPrevious : function(keepExisting){
7664         if(this.last){
7665             this.selectRow(this.last-1, keepExisting);
7666             var view = this.grid.view ? this.grid.view : this.grid;
7667             view.focusRow(this.last);
7668         }
7669     },
7670
7671     /**
7672      * Returns the selected records
7673      * @return {Array} Array of selected records
7674      */
7675     getSelections : function(){
7676         return [].concat(this.selections.items);
7677     },
7678
7679     /**
7680      * Returns the first selected record.
7681      * @return {Record}
7682      */
7683     getSelected : function(){
7684         return this.selections.itemAt(0);
7685     },
7686
7687
7688     /**
7689      * Clears all selections.
7690      */
7691     clearSelections : function(fast){
7692         if(this.locked) {
7693             return;
7694         }
7695         if(fast !== true){
7696             var ds = this.grid.ds;
7697             var s = this.selections;
7698             s.each(function(r){
7699                 this.deselectRow(ds.indexOfId(r.id));
7700             }, this);
7701             s.clear();
7702         }else{
7703             this.selections.clear();
7704         }
7705         this.last = false;
7706     },
7707
7708
7709     /**
7710      * Selects all rows.
7711      */
7712     selectAll : function(){
7713         if(this.locked) {
7714             return;
7715         }
7716         this.selections.clear();
7717         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7718             this.selectRow(i, true);
7719         }
7720     },
7721
7722     /**
7723      * Returns True if there is a selection.
7724      * @return {Boolean}
7725      */
7726     hasSelection : function(){
7727         return this.selections.length > 0;
7728     },
7729
7730     /**
7731      * Returns True if the specified row is selected.
7732      * @param {Number/Record} record The record or index of the record to check
7733      * @return {Boolean}
7734      */
7735     isSelected : function(index){
7736         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7737         return (r && this.selections.key(r.id) ? true : false);
7738     },
7739
7740     /**
7741      * Returns True if the specified record id is selected.
7742      * @param {String} id The id of record to check
7743      * @return {Boolean}
7744      */
7745     isIdSelected : function(id){
7746         return (this.selections.key(id) ? true : false);
7747     },
7748
7749     // private
7750     handleMouseDown : function(e, t)
7751     {
7752         var view = this.grid.view ? this.grid.view : this.grid;
7753         var rowIndex;
7754         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7755             return;
7756         };
7757         if(e.shiftKey && this.last !== false){
7758             var last = this.last;
7759             this.selectRange(last, rowIndex, e.ctrlKey);
7760             this.last = last; // reset the last
7761             view.focusRow(rowIndex);
7762         }else{
7763             var isSelected = this.isSelected(rowIndex);
7764             if(e.button !== 0 && isSelected){
7765                 view.focusRow(rowIndex);
7766             }else if(e.ctrlKey && isSelected){
7767                 this.deselectRow(rowIndex);
7768             }else if(!isSelected){
7769                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7770                 view.focusRow(rowIndex);
7771             }
7772         }
7773         this.fireEvent("afterselectionchange", this);
7774     },
7775     // private
7776     handleDragableRowClick :  function(grid, rowIndex, e) 
7777     {
7778         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7779             this.selectRow(rowIndex, false);
7780             var view = this.grid.view ? this.grid.view : this.grid;
7781             view.focusRow(rowIndex);
7782              this.fireEvent("afterselectionchange", this);
7783         }
7784     },
7785     
7786     /**
7787      * Selects multiple rows.
7788      * @param {Array} rows Array of the indexes of the row to select
7789      * @param {Boolean} keepExisting (optional) True to keep existing selections
7790      */
7791     selectRows : function(rows, keepExisting){
7792         if(!keepExisting){
7793             this.clearSelections();
7794         }
7795         for(var i = 0, len = rows.length; i < len; i++){
7796             this.selectRow(rows[i], true);
7797         }
7798     },
7799
7800     /**
7801      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7802      * @param {Number} startRow The index of the first row in the range
7803      * @param {Number} endRow The index of the last row in the range
7804      * @param {Boolean} keepExisting (optional) True to retain existing selections
7805      */
7806     selectRange : function(startRow, endRow, keepExisting){
7807         if(this.locked) {
7808             return;
7809         }
7810         if(!keepExisting){
7811             this.clearSelections();
7812         }
7813         if(startRow <= endRow){
7814             for(var i = startRow; i <= endRow; i++){
7815                 this.selectRow(i, true);
7816             }
7817         }else{
7818             for(var i = startRow; i >= endRow; i--){
7819                 this.selectRow(i, true);
7820             }
7821         }
7822     },
7823
7824     /**
7825      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7826      * @param {Number} startRow The index of the first row in the range
7827      * @param {Number} endRow The index of the last row in the range
7828      */
7829     deselectRange : function(startRow, endRow, preventViewNotify){
7830         if(this.locked) {
7831             return;
7832         }
7833         for(var i = startRow; i <= endRow; i++){
7834             this.deselectRow(i, preventViewNotify);
7835         }
7836     },
7837
7838     /**
7839      * Selects a row.
7840      * @param {Number} row The index of the row to select
7841      * @param {Boolean} keepExisting (optional) True to keep existing selections
7842      */
7843     selectRow : function(index, keepExisting, preventViewNotify){
7844         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7845             return;
7846         }
7847         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7848             if(!keepExisting || this.singleSelect){
7849                 this.clearSelections();
7850             }
7851             var r = this.grid.ds.getAt(index);
7852             this.selections.add(r);
7853             this.last = this.lastActive = index;
7854             if(!preventViewNotify){
7855                 var view = this.grid.view ? this.grid.view : this.grid;
7856                 view.onRowSelect(index);
7857             }
7858             this.fireEvent("rowselect", this, index, r);
7859             this.fireEvent("selectionchange", this);
7860         }
7861     },
7862
7863     /**
7864      * Deselects a row.
7865      * @param {Number} row The index of the row to deselect
7866      */
7867     deselectRow : function(index, preventViewNotify){
7868         if(this.locked) {
7869             return;
7870         }
7871         if(this.last == index){
7872             this.last = false;
7873         }
7874         if(this.lastActive == index){
7875             this.lastActive = false;
7876         }
7877         var r = this.grid.ds.getAt(index);
7878         this.selections.remove(r);
7879         if(!preventViewNotify){
7880             var view = this.grid.view ? this.grid.view : this.grid;
7881             view.onRowDeselect(index);
7882         }
7883         this.fireEvent("rowdeselect", this, index);
7884         this.fireEvent("selectionchange", this);
7885     },
7886
7887     // private
7888     restoreLast : function(){
7889         if(this._last){
7890             this.last = this._last;
7891         }
7892     },
7893
7894     // private
7895     acceptsNav : function(row, col, cm){
7896         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7897     },
7898
7899     // private
7900     onEditorKey : function(field, e){
7901         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7902         if(k == e.TAB){
7903             e.stopEvent();
7904             ed.completeEdit();
7905             if(e.shiftKey){
7906                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7907             }else{
7908                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7909             }
7910         }else if(k == e.ENTER && !e.ctrlKey){
7911             e.stopEvent();
7912             ed.completeEdit();
7913             if(e.shiftKey){
7914                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7915             }else{
7916                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7917             }
7918         }else if(k == e.ESC){
7919             ed.cancelEdit();
7920         }
7921         if(newCell){
7922             g.startEditing(newCell[0], newCell[1]);
7923         }
7924     }
7925 });/*
7926  * Based on:
7927  * Ext JS Library 1.1.1
7928  * Copyright(c) 2006-2007, Ext JS, LLC.
7929  *
7930  * Originally Released Under LGPL - original licence link has changed is not relivant.
7931  *
7932  * Fork - LGPL
7933  * <script type="text/javascript">
7934  */
7935  
7936
7937 /**
7938  * @class Roo.grid.ColumnModel
7939  * @extends Roo.util.Observable
7940  * This is the default implementation of a ColumnModel used by the Grid. It defines
7941  * the columns in the grid.
7942  * <br>Usage:<br>
7943  <pre><code>
7944  var colModel = new Roo.grid.ColumnModel([
7945         {header: "Ticker", width: 60, sortable: true, locked: true},
7946         {header: "Company Name", width: 150, sortable: true},
7947         {header: "Market Cap.", width: 100, sortable: true},
7948         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7949         {header: "Employees", width: 100, sortable: true, resizable: false}
7950  ]);
7951  </code></pre>
7952  * <p>
7953  
7954  * The config options listed for this class are options which may appear in each
7955  * individual column definition.
7956  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7957  * @constructor
7958  * @param {Object} config An Array of column config objects. See this class's
7959  * config objects for details.
7960 */
7961 Roo.grid.ColumnModel = function(config){
7962         /**
7963      * The config passed into the constructor
7964      */
7965     this.config = []; //config;
7966     this.lookup = {};
7967
7968     // if no id, create one
7969     // if the column does not have a dataIndex mapping,
7970     // map it to the order it is in the config
7971     for(var i = 0, len = config.length; i < len; i++){
7972         this.addColumn(config[i]);
7973         
7974     }
7975
7976     /**
7977      * The width of columns which have no width specified (defaults to 100)
7978      * @type Number
7979      */
7980     this.defaultWidth = 100;
7981
7982     /**
7983      * Default sortable of columns which have no sortable specified (defaults to false)
7984      * @type Boolean
7985      */
7986     this.defaultSortable = false;
7987
7988     this.addEvents({
7989         /**
7990              * @event widthchange
7991              * Fires when the width of a column changes.
7992              * @param {ColumnModel} this
7993              * @param {Number} columnIndex The column index
7994              * @param {Number} newWidth The new width
7995              */
7996             "widthchange": true,
7997         /**
7998              * @event headerchange
7999              * Fires when the text of a header changes.
8000              * @param {ColumnModel} this
8001              * @param {Number} columnIndex The column index
8002              * @param {Number} newText The new header text
8003              */
8004             "headerchange": true,
8005         /**
8006              * @event hiddenchange
8007              * Fires when a column is hidden or "unhidden".
8008              * @param {ColumnModel} this
8009              * @param {Number} columnIndex The column index
8010              * @param {Boolean} hidden true if hidden, false otherwise
8011              */
8012             "hiddenchange": true,
8013             /**
8014          * @event columnmoved
8015          * Fires when a column is moved.
8016          * @param {ColumnModel} this
8017          * @param {Number} oldIndex
8018          * @param {Number} newIndex
8019          */
8020         "columnmoved" : true,
8021         /**
8022          * @event columlockchange
8023          * Fires when a column's locked state is changed
8024          * @param {ColumnModel} this
8025          * @param {Number} colIndex
8026          * @param {Boolean} locked true if locked
8027          */
8028         "columnlockchange" : true
8029     });
8030     Roo.grid.ColumnModel.superclass.constructor.call(this);
8031 };
8032 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8033     /**
8034      * @cfg {String} header The header text to display in the Grid view.
8035      */
8036         /**
8037      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8038      */
8039         /**
8040      * @cfg {String} smHeader Header at Bootsrap Small width
8041      */
8042         /**
8043      * @cfg {String} mdHeader Header at Bootsrap Medium width
8044      */
8045         /**
8046      * @cfg {String} lgHeader Header at Bootsrap Large width
8047      */
8048         /**
8049      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8050      */
8051     /**
8052      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8053      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8054      * specified, the column's index is used as an index into the Record's data Array.
8055      */
8056     /**
8057      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8058      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8059      */
8060     /**
8061      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8062      * Defaults to the value of the {@link #defaultSortable} property.
8063      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8064      */
8065     /**
8066      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8067      */
8068     /**
8069      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8070      */
8071     /**
8072      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8073      */
8074     /**
8075      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8076      */
8077     /**
8078      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8079      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8080      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8081      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8082      */
8083        /**
8084      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8085      */
8086     /**
8087      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8088      */
8089     /**
8090      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8091      */
8092     /**
8093      * @cfg {String} cursor (Optional)
8094      */
8095     /**
8096      * @cfg {String} tooltip (Optional)
8097      */
8098     /**
8099      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8100      */
8101     /**
8102      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8103      */
8104     /**
8105      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8106      */
8107     /**
8108      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8109      */
8110         /**
8111      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8112      */
8113     /**
8114      * Returns the id of the column at the specified index.
8115      * @param {Number} index The column index
8116      * @return {String} the id
8117      */
8118     getColumnId : function(index){
8119         return this.config[index].id;
8120     },
8121
8122     /**
8123      * Returns the column for a specified id.
8124      * @param {String} id The column id
8125      * @return {Object} the column
8126      */
8127     getColumnById : function(id){
8128         return this.lookup[id];
8129     },
8130
8131     
8132     /**
8133      * Returns the column Object for a specified dataIndex.
8134      * @param {String} dataIndex The column dataIndex
8135      * @return {Object|Boolean} the column or false if not found
8136      */
8137     getColumnByDataIndex: function(dataIndex){
8138         var index = this.findColumnIndex(dataIndex);
8139         return index > -1 ? this.config[index] : false;
8140     },
8141     
8142     /**
8143      * Returns the index for a specified column id.
8144      * @param {String} id The column id
8145      * @return {Number} the index, or -1 if not found
8146      */
8147     getIndexById : function(id){
8148         for(var i = 0, len = this.config.length; i < len; i++){
8149             if(this.config[i].id == id){
8150                 return i;
8151             }
8152         }
8153         return -1;
8154     },
8155     
8156     /**
8157      * Returns the index for a specified column dataIndex.
8158      * @param {String} dataIndex The column dataIndex
8159      * @return {Number} the index, or -1 if not found
8160      */
8161     
8162     findColumnIndex : function(dataIndex){
8163         for(var i = 0, len = this.config.length; i < len; i++){
8164             if(this.config[i].dataIndex == dataIndex){
8165                 return i;
8166             }
8167         }
8168         return -1;
8169     },
8170     
8171     
8172     moveColumn : function(oldIndex, newIndex){
8173         var c = this.config[oldIndex];
8174         this.config.splice(oldIndex, 1);
8175         this.config.splice(newIndex, 0, c);
8176         this.dataMap = null;
8177         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8178     },
8179
8180     isLocked : function(colIndex){
8181         return this.config[colIndex].locked === true;
8182     },
8183
8184     setLocked : function(colIndex, value, suppressEvent){
8185         if(this.isLocked(colIndex) == value){
8186             return;
8187         }
8188         this.config[colIndex].locked = value;
8189         if(!suppressEvent){
8190             this.fireEvent("columnlockchange", this, colIndex, value);
8191         }
8192     },
8193
8194     getTotalLockedWidth : function(){
8195         var totalWidth = 0;
8196         for(var i = 0; i < this.config.length; i++){
8197             if(this.isLocked(i) && !this.isHidden(i)){
8198                 this.totalWidth += this.getColumnWidth(i);
8199             }
8200         }
8201         return totalWidth;
8202     },
8203
8204     getLockedCount : function(){
8205         for(var i = 0, len = this.config.length; i < len; i++){
8206             if(!this.isLocked(i)){
8207                 return i;
8208             }
8209         }
8210         
8211         return this.config.length;
8212     },
8213
8214     /**
8215      * Returns the number of columns.
8216      * @return {Number}
8217      */
8218     getColumnCount : function(visibleOnly){
8219         if(visibleOnly === true){
8220             var c = 0;
8221             for(var i = 0, len = this.config.length; i < len; i++){
8222                 if(!this.isHidden(i)){
8223                     c++;
8224                 }
8225             }
8226             return c;
8227         }
8228         return this.config.length;
8229     },
8230
8231     /**
8232      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8233      * @param {Function} fn
8234      * @param {Object} scope (optional)
8235      * @return {Array} result
8236      */
8237     getColumnsBy : function(fn, scope){
8238         var r = [];
8239         for(var i = 0, len = this.config.length; i < len; i++){
8240             var c = this.config[i];
8241             if(fn.call(scope||this, c, i) === true){
8242                 r[r.length] = c;
8243             }
8244         }
8245         return r;
8246     },
8247
8248     /**
8249      * Returns true if the specified column is sortable.
8250      * @param {Number} col The column index
8251      * @return {Boolean}
8252      */
8253     isSortable : function(col){
8254         if(typeof this.config[col].sortable == "undefined"){
8255             return this.defaultSortable;
8256         }
8257         return this.config[col].sortable;
8258     },
8259
8260     /**
8261      * Returns the rendering (formatting) function defined for the column.
8262      * @param {Number} col The column index.
8263      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8264      */
8265     getRenderer : function(col){
8266         if(!this.config[col].renderer){
8267             return Roo.grid.ColumnModel.defaultRenderer;
8268         }
8269         return this.config[col].renderer;
8270     },
8271
8272     /**
8273      * Sets the rendering (formatting) function for a column.
8274      * @param {Number} col The column index
8275      * @param {Function} fn The function to use to process the cell's raw data
8276      * to return HTML markup for the grid view. The render function is called with
8277      * the following parameters:<ul>
8278      * <li>Data value.</li>
8279      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8280      * <li>css A CSS style string to apply to the table cell.</li>
8281      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8282      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8283      * <li>Row index</li>
8284      * <li>Column index</li>
8285      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8286      */
8287     setRenderer : function(col, fn){
8288         this.config[col].renderer = fn;
8289     },
8290
8291     /**
8292      * Returns the width for the specified column.
8293      * @param {Number} col The column index
8294      * @param (optional) {String} gridSize bootstrap width size.
8295      * @return {Number}
8296      */
8297     getColumnWidth : function(col, gridSize)
8298         {
8299                 var cfg = this.config[col];
8300                 
8301                 if (typeof(gridSize) == 'undefined') {
8302                         return cfg.width * 1 || this.defaultWidth;
8303                 }
8304                 if (gridSize === false) { // if we set it..
8305                         return cfg.width || false;
8306                 }
8307                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8308                 
8309                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8310                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8311                                 continue;
8312                         }
8313                         return cfg[ sizes[i] ];
8314                 }
8315                 return 1;
8316                 
8317     },
8318
8319     /**
8320      * Sets the width for a column.
8321      * @param {Number} col The column index
8322      * @param {Number} width The new width
8323      */
8324     setColumnWidth : function(col, width, suppressEvent){
8325         this.config[col].width = width;
8326         this.totalWidth = null;
8327         if(!suppressEvent){
8328              this.fireEvent("widthchange", this, col, width);
8329         }
8330     },
8331
8332     /**
8333      * Returns the total width of all columns.
8334      * @param {Boolean} includeHidden True to include hidden column widths
8335      * @return {Number}
8336      */
8337     getTotalWidth : function(includeHidden){
8338         if(!this.totalWidth){
8339             this.totalWidth = 0;
8340             for(var i = 0, len = this.config.length; i < len; i++){
8341                 if(includeHidden || !this.isHidden(i)){
8342                     this.totalWidth += this.getColumnWidth(i);
8343                 }
8344             }
8345         }
8346         return this.totalWidth;
8347     },
8348
8349     /**
8350      * Returns the header for the specified column.
8351      * @param {Number} col The column index
8352      * @return {String}
8353      */
8354     getColumnHeader : function(col){
8355         return this.config[col].header;
8356     },
8357
8358     /**
8359      * Sets the header for a column.
8360      * @param {Number} col The column index
8361      * @param {String} header The new header
8362      */
8363     setColumnHeader : function(col, header){
8364         this.config[col].header = header;
8365         this.fireEvent("headerchange", this, col, header);
8366     },
8367
8368     /**
8369      * Returns the tooltip for the specified column.
8370      * @param {Number} col The column index
8371      * @return {String}
8372      */
8373     getColumnTooltip : function(col){
8374             return this.config[col].tooltip;
8375     },
8376     /**
8377      * Sets the tooltip for a column.
8378      * @param {Number} col The column index
8379      * @param {String} tooltip The new tooltip
8380      */
8381     setColumnTooltip : function(col, tooltip){
8382             this.config[col].tooltip = tooltip;
8383     },
8384
8385     /**
8386      * Returns the dataIndex for the specified column.
8387      * @param {Number} col The column index
8388      * @return {Number}
8389      */
8390     getDataIndex : function(col){
8391         return this.config[col].dataIndex;
8392     },
8393
8394     /**
8395      * Sets the dataIndex for a column.
8396      * @param {Number} col The column index
8397      * @param {Number} dataIndex The new dataIndex
8398      */
8399     setDataIndex : function(col, dataIndex){
8400         this.config[col].dataIndex = dataIndex;
8401     },
8402
8403     
8404     
8405     /**
8406      * Returns true if the cell is editable.
8407      * @param {Number} colIndex The column index
8408      * @param {Number} rowIndex The row index - this is nto actually used..?
8409      * @return {Boolean}
8410      */
8411     isCellEditable : function(colIndex, rowIndex){
8412         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8413     },
8414
8415     /**
8416      * Returns the editor defined for the cell/column.
8417      * return false or null to disable editing.
8418      * @param {Number} colIndex The column index
8419      * @param {Number} rowIndex The row index
8420      * @return {Object}
8421      */
8422     getCellEditor : function(colIndex, rowIndex){
8423         return this.config[colIndex].editor;
8424     },
8425
8426     /**
8427      * Sets if a column is editable.
8428      * @param {Number} col The column index
8429      * @param {Boolean} editable True if the column is editable
8430      */
8431     setEditable : function(col, editable){
8432         this.config[col].editable = editable;
8433     },
8434
8435
8436     /**
8437      * Returns true if the column is hidden.
8438      * @param {Number} colIndex The column index
8439      * @return {Boolean}
8440      */
8441     isHidden : function(colIndex){
8442         return this.config[colIndex].hidden;
8443     },
8444
8445
8446     /**
8447      * Returns true if the column width cannot be changed
8448      */
8449     isFixed : function(colIndex){
8450         return this.config[colIndex].fixed;
8451     },
8452
8453     /**
8454      * Returns true if the column can be resized
8455      * @return {Boolean}
8456      */
8457     isResizable : function(colIndex){
8458         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8459     },
8460     /**
8461      * Sets if a column is hidden.
8462      * @param {Number} colIndex The column index
8463      * @param {Boolean} hidden True if the column is hidden
8464      */
8465     setHidden : function(colIndex, hidden){
8466         this.config[colIndex].hidden = hidden;
8467         this.totalWidth = null;
8468         this.fireEvent("hiddenchange", this, colIndex, hidden);
8469     },
8470
8471     /**
8472      * Sets the editor for a column.
8473      * @param {Number} col The column index
8474      * @param {Object} editor The editor object
8475      */
8476     setEditor : function(col, editor){
8477         this.config[col].editor = editor;
8478     },
8479     /**
8480      * Add a column (experimental...) - defaults to adding to the end..
8481      * @param {Object} config 
8482     */
8483     addColumn : function(c)
8484     {
8485     
8486         var i = this.config.length;
8487         this.config[i] = c;
8488         
8489         if(typeof c.dataIndex == "undefined"){
8490             c.dataIndex = i;
8491         }
8492         if(typeof c.renderer == "string"){
8493             c.renderer = Roo.util.Format[c.renderer];
8494         }
8495         if(typeof c.id == "undefined"){
8496             c.id = Roo.id();
8497         }
8498         if(c.editor && c.editor.xtype){
8499             c.editor  = Roo.factory(c.editor, Roo.grid);
8500         }
8501         if(c.editor && c.editor.isFormField){
8502             c.editor = new Roo.grid.GridEditor(c.editor);
8503         }
8504         this.lookup[c.id] = c;
8505     }
8506     
8507 });
8508
8509 Roo.grid.ColumnModel.defaultRenderer = function(value)
8510 {
8511     if(typeof value == "object") {
8512         return value;
8513     }
8514         if(typeof value == "string" && value.length < 1){
8515             return "&#160;";
8516         }
8517     
8518         return String.format("{0}", value);
8519 };
8520
8521 // Alias for backwards compatibility
8522 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8523 /*
8524  * Based on:
8525  * Ext JS Library 1.1.1
8526  * Copyright(c) 2006-2007, Ext JS, LLC.
8527  *
8528  * Originally Released Under LGPL - original licence link has changed is not relivant.
8529  *
8530  * Fork - LGPL
8531  * <script type="text/javascript">
8532  */
8533  
8534 /**
8535  * @class Roo.LoadMask
8536  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8537  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8538  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8539  * element's UpdateManager load indicator and will be destroyed after the initial load.
8540  * @constructor
8541  * Create a new LoadMask
8542  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8543  * @param {Object} config The config object
8544  */
8545 Roo.LoadMask = function(el, config){
8546     this.el = Roo.get(el);
8547     Roo.apply(this, config);
8548     if(this.store){
8549         this.store.on('beforeload', this.onBeforeLoad, this);
8550         this.store.on('load', this.onLoad, this);
8551         this.store.on('loadexception', this.onLoadException, this);
8552         this.removeMask = false;
8553     }else{
8554         var um = this.el.getUpdateManager();
8555         um.showLoadIndicator = false; // disable the default indicator
8556         um.on('beforeupdate', this.onBeforeLoad, this);
8557         um.on('update', this.onLoad, this);
8558         um.on('failure', this.onLoad, this);
8559         this.removeMask = true;
8560     }
8561 };
8562
8563 Roo.LoadMask.prototype = {
8564     /**
8565      * @cfg {Boolean} removeMask
8566      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8567      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8568      */
8569     /**
8570      * @cfg {String} msg
8571      * The text to display in a centered loading message box (defaults to 'Loading...')
8572      */
8573     msg : 'Loading...',
8574     /**
8575      * @cfg {String} msgCls
8576      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8577      */
8578     msgCls : 'x-mask-loading',
8579
8580     /**
8581      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8582      * @type Boolean
8583      */
8584     disabled: false,
8585
8586     /**
8587      * Disables the mask to prevent it from being displayed
8588      */
8589     disable : function(){
8590        this.disabled = true;
8591     },
8592
8593     /**
8594      * Enables the mask so that it can be displayed
8595      */
8596     enable : function(){
8597         this.disabled = false;
8598     },
8599     
8600     onLoadException : function()
8601     {
8602         Roo.log(arguments);
8603         
8604         if (typeof(arguments[3]) != 'undefined') {
8605             Roo.MessageBox.alert("Error loading",arguments[3]);
8606         } 
8607         /*
8608         try {
8609             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8610                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8611             }   
8612         } catch(e) {
8613             
8614         }
8615         */
8616     
8617         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8618     },
8619     // private
8620     onLoad : function()
8621     {
8622         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8623     },
8624
8625     // private
8626     onBeforeLoad : function(){
8627         if(!this.disabled){
8628             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8629         }
8630     },
8631
8632     // private
8633     destroy : function(){
8634         if(this.store){
8635             this.store.un('beforeload', this.onBeforeLoad, this);
8636             this.store.un('load', this.onLoad, this);
8637             this.store.un('loadexception', this.onLoadException, this);
8638         }else{
8639             var um = this.el.getUpdateManager();
8640             um.un('beforeupdate', this.onBeforeLoad, this);
8641             um.un('update', this.onLoad, this);
8642             um.un('failure', this.onLoad, this);
8643         }
8644     }
8645 };/**
8646  * @class Roo.bootstrap.Table
8647  * @licence LGBL
8648  * @extends Roo.bootstrap.Component
8649  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8650  * Similar to Roo.grid.Grid
8651  * <pre><code>
8652  var table = Roo.factory({
8653     xtype : 'Table',
8654     xns : Roo.bootstrap,
8655     autoSizeColumns: true,
8656     
8657     
8658     store : {
8659         xtype : 'Store',
8660         xns : Roo.data,
8661         remoteSort : true,
8662         sortInfo : { direction : 'ASC', field: 'name' },
8663         proxy : {
8664            xtype : 'HttpProxy',
8665            xns : Roo.data,
8666            method : 'GET',
8667            url : 'https://example.com/some.data.url.json'
8668         },
8669         reader : {
8670            xtype : 'JsonReader',
8671            xns : Roo.data,
8672            fields : [ 'id', 'name', whatever' ],
8673            id : 'id',
8674            root : 'data'
8675         }
8676     },
8677     cm : [
8678         {
8679             xtype : 'ColumnModel',
8680             xns : Roo.grid,
8681             align : 'center',
8682             cursor : 'pointer',
8683             dataIndex : 'is_in_group',
8684             header : "Name",
8685             sortable : true,
8686             renderer : function(v, x , r) {  
8687             
8688                 return String.format("{0}", v)
8689             }
8690             width : 3
8691         } // more columns..
8692     ],
8693     selModel : {
8694         xtype : 'RowSelectionModel',
8695         xns : Roo.bootstrap.Table
8696         // you can add listeners to catch selection change here....
8697     }
8698      
8699
8700  });
8701  // set any options
8702  grid.render(Roo.get("some-div"));
8703 </code></pre>
8704
8705 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8706
8707
8708
8709  *
8710  * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8711  * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8712  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8713  * 
8714  * @cfg {String} cls table class
8715  *
8716  * 
8717  * @cfg {boolean} striped Should the rows be alternative striped
8718  * @cfg {boolean} bordered Add borders to the table
8719  * @cfg {boolean} hover Add hover highlighting
8720  * @cfg {boolean} condensed Format condensed
8721  * @cfg {boolean} responsive Format condensed
8722  * @cfg {Boolean} loadMask (true|false) default false
8723  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8724  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8725  * @cfg {Boolean} rowSelection (true|false) default false
8726  * @cfg {Boolean} cellSelection (true|false) default false
8727  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8728  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8729  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8730  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8731  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8732  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8733  * 
8734  * @constructor
8735  * Create a new Table
8736  * @param {Object} config The config object
8737  */
8738
8739 Roo.bootstrap.Table = function(config)
8740 {
8741     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8742      
8743     // BC...
8744     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8745     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8746     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8747     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8748     
8749     this.view = this; // compat with grid.
8750     
8751     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8752     if (this.sm) {
8753         this.sm.grid = this;
8754         this.selModel = Roo.factory(this.sm, Roo.grid);
8755         this.sm = this.selModel;
8756         this.sm.xmodule = this.xmodule || false;
8757     }
8758     
8759     if (this.cm && typeof(this.cm.config) == 'undefined') {
8760         this.colModel = new Roo.grid.ColumnModel(this.cm);
8761         this.cm = this.colModel;
8762         this.cm.xmodule = this.xmodule || false;
8763     }
8764     if (this.store) {
8765         this.store= Roo.factory(this.store, Roo.data);
8766         this.ds = this.store;
8767         this.ds.xmodule = this.xmodule || false;
8768          
8769     }
8770     if (this.footer && this.store) {
8771         this.footer.dataSource = this.ds;
8772         this.footer = Roo.factory(this.footer);
8773     }
8774     
8775     /** @private */
8776     this.addEvents({
8777         /**
8778          * @event cellclick
8779          * Fires when a cell is clicked
8780          * @param {Roo.bootstrap.Table} this
8781          * @param {Roo.Element} el
8782          * @param {Number} rowIndex
8783          * @param {Number} columnIndex
8784          * @param {Roo.EventObject} e
8785          */
8786         "cellclick" : true,
8787         /**
8788          * @event celldblclick
8789          * Fires when a cell is double clicked
8790          * @param {Roo.bootstrap.Table} this
8791          * @param {Roo.Element} el
8792          * @param {Number} rowIndex
8793          * @param {Number} columnIndex
8794          * @param {Roo.EventObject} e
8795          */
8796         "celldblclick" : true,
8797         /**
8798          * @event rowclick
8799          * Fires when a row is clicked
8800          * @param {Roo.bootstrap.Table} this
8801          * @param {Roo.Element} el
8802          * @param {Number} rowIndex
8803          * @param {Roo.EventObject} e
8804          */
8805         "rowclick" : true,
8806         /**
8807          * @event rowdblclick
8808          * Fires when a row is double clicked
8809          * @param {Roo.bootstrap.Table} this
8810          * @param {Roo.Element} el
8811          * @param {Number} rowIndex
8812          * @param {Roo.EventObject} e
8813          */
8814         "rowdblclick" : true,
8815         /**
8816          * @event mouseover
8817          * Fires when a mouseover occur
8818          * @param {Roo.bootstrap.Table} this
8819          * @param {Roo.Element} el
8820          * @param {Number} rowIndex
8821          * @param {Number} columnIndex
8822          * @param {Roo.EventObject} e
8823          */
8824         "mouseover" : true,
8825         /**
8826          * @event mouseout
8827          * Fires when a mouseout occur
8828          * @param {Roo.bootstrap.Table} this
8829          * @param {Roo.Element} el
8830          * @param {Number} rowIndex
8831          * @param {Number} columnIndex
8832          * @param {Roo.EventObject} e
8833          */
8834         "mouseout" : true,
8835         /**
8836          * @event rowclass
8837          * Fires when a row is rendered, so you can change add a style to it.
8838          * @param {Roo.bootstrap.Table} this
8839          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8840          */
8841         'rowclass' : true,
8842           /**
8843          * @event rowsrendered
8844          * Fires when all the  rows have been rendered
8845          * @param {Roo.bootstrap.Table} this
8846          */
8847         'rowsrendered' : true,
8848         /**
8849          * @event contextmenu
8850          * The raw contextmenu event for the entire grid.
8851          * @param {Roo.EventObject} e
8852          */
8853         "contextmenu" : true,
8854         /**
8855          * @event rowcontextmenu
8856          * Fires when a row is right clicked
8857          * @param {Roo.bootstrap.Table} this
8858          * @param {Number} rowIndex
8859          * @param {Roo.EventObject} e
8860          */
8861         "rowcontextmenu" : true,
8862         /**
8863          * @event cellcontextmenu
8864          * Fires when a cell is right clicked
8865          * @param {Roo.bootstrap.Table} this
8866          * @param {Number} rowIndex
8867          * @param {Number} cellIndex
8868          * @param {Roo.EventObject} e
8869          */
8870          "cellcontextmenu" : true,
8871          /**
8872          * @event headercontextmenu
8873          * Fires when a header is right clicked
8874          * @param {Roo.bootstrap.Table} this
8875          * @param {Number} columnIndex
8876          * @param {Roo.EventObject} e
8877          */
8878         "headercontextmenu" : true,
8879         /**
8880          * @event mousedown
8881          * The raw mousedown event for the entire grid.
8882          * @param {Roo.EventObject} e
8883          */
8884         "mousedown" : true
8885         
8886     });
8887 };
8888
8889 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8890     
8891     cls: false,
8892     
8893     striped : false,
8894     scrollBody : false,
8895     bordered: false,
8896     hover:  false,
8897     condensed : false,
8898     responsive : false,
8899     sm : false,
8900     cm : false,
8901     store : false,
8902     loadMask : false,
8903     footerShow : true,
8904     headerShow : true,
8905     enableColumnResize: true,
8906   
8907     rowSelection : false,
8908     cellSelection : false,
8909     layout : false,
8910
8911     minColumnWidth : 50,
8912     
8913     // Roo.Element - the tbody
8914     bodyEl: false,  // <tbody> Roo.Element - thead element    
8915     headEl: false,  // <thead> Roo.Element - thead element
8916     resizeProxy : false, // proxy element for dragging?
8917
8918
8919     
8920     container: false, // used by gridpanel...
8921     
8922     lazyLoad : false,
8923     
8924     CSS : Roo.util.CSS,
8925     
8926     auto_hide_footer : false,
8927     
8928     view: false, // actually points to this..
8929     
8930     getAutoCreate : function()
8931     {
8932         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8933         
8934         cfg = {
8935             tag: 'table',
8936             cls : 'table', 
8937             cn : []
8938         };
8939         // this get's auto added by panel.Grid
8940         if (this.scrollBody) {
8941             cfg.cls += ' table-body-fixed';
8942         }    
8943         if (this.striped) {
8944             cfg.cls += ' table-striped';
8945         }
8946         
8947         if (this.hover) {
8948             cfg.cls += ' table-hover';
8949         }
8950         if (this.bordered) {
8951             cfg.cls += ' table-bordered';
8952         }
8953         if (this.condensed) {
8954             cfg.cls += ' table-condensed';
8955         }
8956         
8957         if (this.responsive) {
8958             cfg.cls += ' table-responsive';
8959         }
8960         
8961         if (this.cls) {
8962             cfg.cls+=  ' ' +this.cls;
8963         }
8964         
8965         
8966         
8967         if (this.layout) {
8968             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8969         }
8970         
8971         if(this.store || this.cm){
8972             if(this.headerShow){
8973                 cfg.cn.push(this.renderHeader());
8974             }
8975             
8976             cfg.cn.push(this.renderBody());
8977             
8978             if(this.footerShow){
8979                 cfg.cn.push(this.renderFooter());
8980             }
8981             // where does this come from?
8982             //cfg.cls+=  ' TableGrid';
8983         }
8984         
8985         return { cn : [ cfg ] };
8986     },
8987     
8988     initEvents : function()
8989     {   
8990         if(!this.store || !this.cm){
8991             return;
8992         }
8993         if (this.selModel) {
8994             this.selModel.initEvents();
8995         }
8996         
8997         
8998         //Roo.log('initEvents with ds!!!!');
8999         
9000         this.bodyEl = this.el.select('tbody', true).first();
9001         this.headEl = this.el.select('thead', true).first();
9002         this.mainFoot = this.el.select('tfoot', true).first();
9003         
9004         
9005         
9006         
9007         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9008             e.on('click', this.sort, this);
9009         }, this);
9010         
9011         
9012         // why is this done????? = it breaks dialogs??
9013         //this.parent().el.setStyle('position', 'relative');
9014         
9015         
9016         if (this.footer) {
9017             this.footer.parentId = this.id;
9018             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9019             
9020             if(this.lazyLoad){
9021                 this.el.select('tfoot tr td').first().addClass('hide');
9022             }
9023         } 
9024         
9025         if(this.loadMask) {
9026             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9027         }
9028         
9029         this.store.on('load', this.onLoad, this);
9030         this.store.on('beforeload', this.onBeforeLoad, this);
9031         this.store.on('update', this.onUpdate, this);
9032         this.store.on('add', this.onAdd, this);
9033         this.store.on("clear", this.clear, this);
9034         
9035         this.el.on("contextmenu", this.onContextMenu, this);
9036         
9037         
9038         this.cm.on("headerchange", this.onHeaderChange, this);
9039         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9040
9041  //?? does bodyEl get replaced on render?
9042         this.bodyEl.on("click", this.onClick, this);
9043         this.bodyEl.on("dblclick", this.onDblClick, this);        
9044         this.bodyEl.on('scroll', this.onBodyScroll, this);
9045
9046         // guessing mainbody will work - this relays usually caught by selmodel at present.
9047         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9048   
9049   
9050         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9051         
9052   
9053         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9054             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9055         }
9056         
9057         this.initCSS();
9058     },
9059     // Compatibility with grid - we implement all the view features at present.
9060     getView : function()
9061     {
9062         return this;
9063     },
9064     
9065     initCSS : function()
9066     {
9067         
9068         
9069         var cm = this.cm, styles = [];
9070         this.CSS.removeStyleSheet(this.id + '-cssrules');
9071         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9072         // we can honour xs/sm/md/xl  as widths...
9073         // we first have to decide what widht we are currently at...
9074         var sz = Roo.getGridSize();
9075         
9076         var total = 0;
9077         var last = -1;
9078         var cols = []; // visable cols.
9079         var total_abs = 0;
9080         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9081             var w = cm.getColumnWidth(i, false);
9082             if(cm.isHidden(i)){
9083                 cols.push( { rel : false, abs : 0 });
9084                 continue;
9085             }
9086             if (w !== false) {
9087                 cols.push( { rel : false, abs : w });
9088                 total_abs += w;
9089                 last = i; // not really..
9090                 continue;
9091             }
9092             var w = cm.getColumnWidth(i, sz);
9093             if (w > 0) {
9094                 last = i
9095             }
9096             total += w;
9097             cols.push( { rel : w, abs : false });
9098         }
9099         
9100         var avail = this.bodyEl.dom.clientWidth - total_abs;
9101         
9102         var unitWidth = Math.floor(avail / total);
9103         var rem = avail - (unitWidth * total);
9104         
9105         var hidden, width, pos = 0 , splithide , left;
9106         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9107             
9108             hidden = 'display:none;';
9109             left = '';
9110             width  = 'width:0px;';
9111             splithide = '';
9112             if(!cm.isHidden(i)){
9113                 hidden = '';
9114                 
9115                 
9116                 // we can honour xs/sm/md/xl ?
9117                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9118                 if (w===0) {
9119                     hidden = 'display:none;';
9120                 }
9121                 // width should return a small number...
9122                 if (i == last) {
9123                     w+=rem; // add the remaining with..
9124                 }
9125                 pos += w;
9126                 left = "left:" + (pos -4) + "px;";
9127                 width = "width:" + w+ "px;";
9128                 
9129             }
9130             if (this.responsive) {
9131                 width = '';
9132                 left = '';
9133                 splithide = 'display: none';
9134             }
9135             
9136             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9137             if (this.headEl) {
9138                 if (i == last) {
9139                     splithide = 'display:none;';
9140                 }
9141                 
9142                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9143                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9144                 );
9145             }
9146             
9147         }
9148         //Roo.log(styles.join(''));
9149         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9150         
9151     },
9152     
9153     
9154     
9155     onContextMenu : function(e, t)
9156     {
9157         this.processEvent("contextmenu", e);
9158     },
9159     
9160     processEvent : function(name, e)
9161     {
9162         if (name != 'touchstart' ) {
9163             this.fireEvent(name, e);    
9164         }
9165         
9166         var t = e.getTarget();
9167         
9168         var cell = Roo.get(t);
9169         
9170         if(!cell){
9171             return;
9172         }
9173         
9174         if(cell.findParent('tfoot', false, true)){
9175             return;
9176         }
9177         
9178         if(cell.findParent('thead', false, true)){
9179             
9180             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9181                 cell = Roo.get(t).findParent('th', false, true);
9182                 if (!cell) {
9183                     Roo.log("failed to find th in thead?");
9184                     Roo.log(e.getTarget());
9185                     return;
9186                 }
9187             }
9188             
9189             var cellIndex = cell.dom.cellIndex;
9190             
9191             var ename = name == 'touchstart' ? 'click' : name;
9192             this.fireEvent("header" + ename, this, cellIndex, e);
9193             
9194             return;
9195         }
9196         
9197         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9198             cell = Roo.get(t).findParent('td', false, true);
9199             if (!cell) {
9200                 Roo.log("failed to find th in tbody?");
9201                 Roo.log(e.getTarget());
9202                 return;
9203             }
9204         }
9205         
9206         var row = cell.findParent('tr', false, true);
9207         var cellIndex = cell.dom.cellIndex;
9208         var rowIndex = row.dom.rowIndex - 1;
9209         
9210         if(row !== false){
9211             
9212             this.fireEvent("row" + name, this, rowIndex, e);
9213             
9214             if(cell !== false){
9215             
9216                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9217             }
9218         }
9219         
9220     },
9221     
9222     onMouseover : function(e, el)
9223     {
9224         var cell = Roo.get(el);
9225         
9226         if(!cell){
9227             return;
9228         }
9229         
9230         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9231             cell = cell.findParent('td', false, true);
9232         }
9233         
9234         var row = cell.findParent('tr', false, true);
9235         var cellIndex = cell.dom.cellIndex;
9236         var rowIndex = row.dom.rowIndex - 1; // start from 0
9237         
9238         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9239         
9240     },
9241     
9242     onMouseout : function(e, el)
9243     {
9244         var cell = Roo.get(el);
9245         
9246         if(!cell){
9247             return;
9248         }
9249         
9250         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9251             cell = cell.findParent('td', false, true);
9252         }
9253         
9254         var row = cell.findParent('tr', false, true);
9255         var cellIndex = cell.dom.cellIndex;
9256         var rowIndex = row.dom.rowIndex - 1; // start from 0
9257         
9258         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9259         
9260     },
9261     
9262     onClick : function(e, el)
9263     {
9264         var cell = Roo.get(el);
9265         
9266         if(!cell || (!this.cellSelection && !this.rowSelection)){
9267             return;
9268         }
9269         
9270         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9271             cell = cell.findParent('td', false, true);
9272         }
9273         
9274         if(!cell || typeof(cell) == 'undefined'){
9275             return;
9276         }
9277         
9278         var row = cell.findParent('tr', false, true);
9279         
9280         if(!row || typeof(row) == 'undefined'){
9281             return;
9282         }
9283         
9284         var cellIndex = cell.dom.cellIndex;
9285         var rowIndex = this.getRowIndex(row);
9286         
9287         // why??? - should these not be based on SelectionModel?
9288         //if(this.cellSelection){
9289             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9290         //}
9291         
9292         //if(this.rowSelection){
9293             this.fireEvent('rowclick', this, row, rowIndex, e);
9294         //}
9295          
9296     },
9297         
9298     onDblClick : function(e,el)
9299     {
9300         var cell = Roo.get(el);
9301         
9302         if(!cell || (!this.cellSelection && !this.rowSelection)){
9303             return;
9304         }
9305         
9306         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9307             cell = cell.findParent('td', false, true);
9308         }
9309         
9310         if(!cell || typeof(cell) == 'undefined'){
9311             return;
9312         }
9313         
9314         var row = cell.findParent('tr', false, true);
9315         
9316         if(!row || typeof(row) == 'undefined'){
9317             return;
9318         }
9319         
9320         var cellIndex = cell.dom.cellIndex;
9321         var rowIndex = this.getRowIndex(row);
9322         
9323         if(this.cellSelection){
9324             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9325         }
9326         
9327         if(this.rowSelection){
9328             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9329         }
9330     },
9331     findRowIndex : function(el)
9332     {
9333         var cell = Roo.get(el);
9334         if(!cell) {
9335             return false;
9336         }
9337         var row = cell.findParent('tr', false, true);
9338         
9339         if(!row || typeof(row) == 'undefined'){
9340             return false;
9341         }
9342         return this.getRowIndex(row);
9343     },
9344     sort : function(e,el)
9345     {
9346         var col = Roo.get(el);
9347         
9348         if(!col.hasClass('sortable')){
9349             return;
9350         }
9351         
9352         var sort = col.attr('sort');
9353         var dir = 'ASC';
9354         
9355         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9356             dir = 'DESC';
9357         }
9358         
9359         this.store.sortInfo = {field : sort, direction : dir};
9360         
9361         if (this.footer) {
9362             Roo.log("calling footer first");
9363             this.footer.onClick('first');
9364         } else {
9365         
9366             this.store.load({ params : { start : 0 } });
9367         }
9368     },
9369     
9370     renderHeader : function()
9371     {
9372         var header = {
9373             tag: 'thead',
9374             cn : []
9375         };
9376         
9377         var cm = this.cm;
9378         this.totalWidth = 0;
9379         
9380         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9381             
9382             var config = cm.config[i];
9383             
9384             var c = {
9385                 tag: 'th',
9386                 cls : 'x-hcol-' + i,
9387                 style : '',
9388                 
9389                 html: cm.getColumnHeader(i)
9390             };
9391             
9392             var tooltip = cm.getColumnTooltip(i);
9393             if (tooltip) {
9394                 c.tooltip = tooltip;
9395             }
9396             
9397             
9398             var hh = '';
9399             
9400             if(typeof(config.sortable) != 'undefined' && config.sortable){
9401                 c.cls += ' sortable';
9402                 c.html = '<i class="fa"></i>' + c.html;
9403             }
9404             
9405             // could use BS4 hidden-..-down 
9406             
9407             if(typeof(config.lgHeader) != 'undefined'){
9408                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9409             }
9410             
9411             if(typeof(config.mdHeader) != 'undefined'){
9412                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9413             }
9414             
9415             if(typeof(config.smHeader) != 'undefined'){
9416                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9417             }
9418             
9419             if(typeof(config.xsHeader) != 'undefined'){
9420                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9421             }
9422             
9423             if(hh.length){
9424                 c.html = hh;
9425             }
9426             
9427             if(typeof(config.tooltip) != 'undefined'){
9428                 c.tooltip = config.tooltip;
9429             }
9430             
9431             if(typeof(config.colspan) != 'undefined'){
9432                 c.colspan = config.colspan;
9433             }
9434             
9435             // hidden is handled by CSS now
9436             
9437             if(typeof(config.dataIndex) != 'undefined'){
9438                 c.sort = config.dataIndex;
9439             }
9440             
9441            
9442             
9443             if(typeof(config.align) != 'undefined' && config.align.length){
9444                 c.style += ' text-align:' + config.align + ';';
9445             }
9446             
9447             /* width is done in CSS
9448              *if(typeof(config.width) != 'undefined'){
9449                 c.style += ' width:' + config.width + 'px;';
9450                 this.totalWidth += config.width;
9451             } else {
9452                 this.totalWidth += 100; // assume minimum of 100 per column?
9453             }
9454             */
9455             
9456             if(typeof(config.cls) != 'undefined'){
9457                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9458             }
9459             // this is the bit that doesnt reall work at all...
9460             
9461             if (this.responsive) {
9462                  
9463             
9464                 ['xs','sm','md','lg'].map(function(size){
9465                     
9466                     if(typeof(config[size]) == 'undefined'){
9467                         return;
9468                     }
9469                      
9470                     if (!config[size]) { // 0 = hidden
9471                         // BS 4 '0' is treated as hide that column and below.
9472                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9473                         return;
9474                     }
9475                     
9476                     c.cls += ' col-' + size + '-' + config[size] + (
9477                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9478                     );
9479                     
9480                     
9481                 });
9482             }
9483             // at the end?
9484             
9485             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9486             
9487             
9488             
9489             
9490             header.cn.push(c)
9491         }
9492         
9493         return header;
9494     },
9495     
9496     renderBody : function()
9497     {
9498         var body = {
9499             tag: 'tbody',
9500             cn : [
9501                 {
9502                     tag: 'tr',
9503                     cn : [
9504                         {
9505                             tag : 'td',
9506                             colspan :  this.cm.getColumnCount()
9507                         }
9508                     ]
9509                 }
9510             ]
9511         };
9512         
9513         return body;
9514     },
9515     
9516     renderFooter : function()
9517     {
9518         var footer = {
9519             tag: 'tfoot',
9520             cn : [
9521                 {
9522                     tag: 'tr',
9523                     cn : [
9524                         {
9525                             tag : 'td',
9526                             colspan :  this.cm.getColumnCount()
9527                         }
9528                     ]
9529                 }
9530             ]
9531         };
9532         
9533         return footer;
9534     },
9535     
9536     
9537     
9538     onLoad : function()
9539     {
9540 //        Roo.log('ds onload');
9541         this.clear();
9542         
9543         var _this = this;
9544         var cm = this.cm;
9545         var ds = this.store;
9546         
9547         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9548             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9549             if (_this.store.sortInfo) {
9550                     
9551                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9552                     e.select('i', true).addClass(['fa-arrow-up']);
9553                 }
9554                 
9555                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9556                     e.select('i', true).addClass(['fa-arrow-down']);
9557                 }
9558             }
9559         });
9560         
9561         var tbody =  this.bodyEl;
9562               
9563         if(ds.getCount() > 0){
9564             ds.data.each(function(d,rowIndex){
9565                 var row =  this.renderRow(cm, ds, rowIndex);
9566                 
9567                 tbody.createChild(row);
9568                 
9569                 var _this = this;
9570                 
9571                 if(row.cellObjects.length){
9572                     Roo.each(row.cellObjects, function(r){
9573                         _this.renderCellObject(r);
9574                     })
9575                 }
9576                 
9577             }, this);
9578         }
9579         
9580         var tfoot = this.el.select('tfoot', true).first();
9581         
9582         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9583             
9584             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9585             
9586             var total = this.ds.getTotalCount();
9587             
9588             if(this.footer.pageSize < total){
9589                 this.mainFoot.show();
9590             }
9591         }
9592         
9593         Roo.each(this.el.select('tbody td', true).elements, function(e){
9594             e.on('mouseover', _this.onMouseover, _this);
9595         });
9596         
9597         Roo.each(this.el.select('tbody td', true).elements, function(e){
9598             e.on('mouseout', _this.onMouseout, _this);
9599         });
9600         this.fireEvent('rowsrendered', this);
9601         
9602         this.autoSize();
9603         
9604         this.initCSS(); /// resize cols
9605
9606         
9607     },
9608     
9609     
9610     onUpdate : function(ds,record)
9611     {
9612         this.refreshRow(record);
9613         this.autoSize();
9614     },
9615     
9616     onRemove : function(ds, record, index, isUpdate){
9617         if(isUpdate !== true){
9618             this.fireEvent("beforerowremoved", this, index, record);
9619         }
9620         var bt = this.bodyEl.dom;
9621         
9622         var rows = this.el.select('tbody > tr', true).elements;
9623         
9624         if(typeof(rows[index]) != 'undefined'){
9625             bt.removeChild(rows[index].dom);
9626         }
9627         
9628 //        if(bt.rows[index]){
9629 //            bt.removeChild(bt.rows[index]);
9630 //        }
9631         
9632         if(isUpdate !== true){
9633             //this.stripeRows(index);
9634             //this.syncRowHeights(index, index);
9635             //this.layout();
9636             this.fireEvent("rowremoved", this, index, record);
9637         }
9638     },
9639     
9640     onAdd : function(ds, records, rowIndex)
9641     {
9642         //Roo.log('on Add called');
9643         // - note this does not handle multiple adding very well..
9644         var bt = this.bodyEl.dom;
9645         for (var i =0 ; i < records.length;i++) {
9646             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9647             //Roo.log(records[i]);
9648             //Roo.log(this.store.getAt(rowIndex+i));
9649             this.insertRow(this.store, rowIndex + i, false);
9650             return;
9651         }
9652         
9653     },
9654     
9655     
9656     refreshRow : function(record){
9657         var ds = this.store, index;
9658         if(typeof record == 'number'){
9659             index = record;
9660             record = ds.getAt(index);
9661         }else{
9662             index = ds.indexOf(record);
9663             if (index < 0) {
9664                 return; // should not happen - but seems to 
9665             }
9666         }
9667         this.insertRow(ds, index, true);
9668         this.autoSize();
9669         this.onRemove(ds, record, index+1, true);
9670         this.autoSize();
9671         //this.syncRowHeights(index, index);
9672         //this.layout();
9673         this.fireEvent("rowupdated", this, index, record);
9674     },
9675     // private - called by RowSelection
9676     onRowSelect : function(rowIndex){
9677         var row = this.getRowDom(rowIndex);
9678         row.addClass(['bg-info','info']);
9679     },
9680     // private - called by RowSelection
9681     onRowDeselect : function(rowIndex)
9682     {
9683         if (rowIndex < 0) {
9684             return;
9685         }
9686         var row = this.getRowDom(rowIndex);
9687         row.removeClass(['bg-info','info']);
9688     },
9689       /**
9690      * Focuses the specified row.
9691      * @param {Number} row The row index
9692      */
9693     focusRow : function(row)
9694     {
9695         //Roo.log('GridView.focusRow');
9696         var x = this.bodyEl.dom.scrollLeft;
9697         this.focusCell(row, 0, false);
9698         this.bodyEl.dom.scrollLeft = x;
9699
9700     },
9701      /**
9702      * Focuses the specified cell.
9703      * @param {Number} row The row index
9704      * @param {Number} col The column index
9705      * @param {Boolean} hscroll false to disable horizontal scrolling
9706      */
9707     focusCell : function(row, col, hscroll)
9708     {
9709         //Roo.log('GridView.focusCell');
9710         var el = this.ensureVisible(row, col, hscroll);
9711         // not sure what focusEL achives = it's a <a> pos relative 
9712         //this.focusEl.alignTo(el, "tl-tl");
9713         //if(Roo.isGecko){
9714         //    this.focusEl.focus();
9715         //}else{
9716         //    this.focusEl.focus.defer(1, this.focusEl);
9717         //}
9718     },
9719     
9720      /**
9721      * Scrolls the specified cell into view
9722      * @param {Number} row The row index
9723      * @param {Number} col The column index
9724      * @param {Boolean} hscroll false to disable horizontal scrolling
9725      */
9726     ensureVisible : function(row, col, hscroll)
9727     {
9728         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9729         //return null; //disable for testing.
9730         if(typeof row != "number"){
9731             row = row.rowIndex;
9732         }
9733         if(row < 0 && row >= this.ds.getCount()){
9734             return  null;
9735         }
9736         col = (col !== undefined ? col : 0);
9737         var cm = this.cm;
9738         while(cm.isHidden(col)){
9739             col++;
9740         }
9741
9742         var el = this.getCellDom(row, col);
9743         if(!el){
9744             return null;
9745         }
9746         var c = this.bodyEl.dom;
9747
9748         var ctop = parseInt(el.offsetTop, 10);
9749         var cleft = parseInt(el.offsetLeft, 10);
9750         var cbot = ctop + el.offsetHeight;
9751         var cright = cleft + el.offsetWidth;
9752
9753         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9754         var ch = 0; //?? header is not withing the area?
9755         var stop = parseInt(c.scrollTop, 10);
9756         var sleft = parseInt(c.scrollLeft, 10);
9757         var sbot = stop + ch;
9758         var sright = sleft + c.clientWidth;
9759         /*
9760         Roo.log('GridView.ensureVisible:' +
9761                 ' ctop:' + ctop +
9762                 ' c.clientHeight:' + c.clientHeight +
9763                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9764                 ' stop:' + stop +
9765                 ' cbot:' + cbot +
9766                 ' sbot:' + sbot +
9767                 ' ch:' + ch  
9768                 );
9769         */
9770         if(ctop < stop){
9771             c.scrollTop = ctop;
9772             //Roo.log("set scrolltop to ctop DISABLE?");
9773         }else if(cbot > sbot){
9774             //Roo.log("set scrolltop to cbot-ch");
9775             c.scrollTop = cbot-ch;
9776         }
9777
9778         if(hscroll !== false){
9779             if(cleft < sleft){
9780                 c.scrollLeft = cleft;
9781             }else if(cright > sright){
9782                 c.scrollLeft = cright-c.clientWidth;
9783             }
9784         }
9785
9786         return el;
9787     },
9788     
9789     
9790     insertRow : function(dm, rowIndex, isUpdate){
9791         
9792         if(!isUpdate){
9793             this.fireEvent("beforerowsinserted", this, rowIndex);
9794         }
9795             //var s = this.getScrollState();
9796         var row = this.renderRow(this.cm, this.store, rowIndex);
9797         // insert before rowIndex..
9798         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9799         
9800         var _this = this;
9801                 
9802         if(row.cellObjects.length){
9803             Roo.each(row.cellObjects, function(r){
9804                 _this.renderCellObject(r);
9805             })
9806         }
9807             
9808         if(!isUpdate){
9809             this.fireEvent("rowsinserted", this, rowIndex);
9810             //this.syncRowHeights(firstRow, lastRow);
9811             //this.stripeRows(firstRow);
9812             //this.layout();
9813         }
9814         
9815     },
9816     
9817     
9818     getRowDom : function(rowIndex)
9819     {
9820         var rows = this.el.select('tbody > tr', true).elements;
9821         
9822         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9823         
9824     },
9825     getCellDom : function(rowIndex, colIndex)
9826     {
9827         var row = this.getRowDom(rowIndex);
9828         if (row === false) {
9829             return false;
9830         }
9831         var cols = row.select('td', true).elements;
9832         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9833         
9834     },
9835     
9836     // returns the object tree for a tr..
9837   
9838     
9839     renderRow : function(cm, ds, rowIndex) 
9840     {
9841         var d = ds.getAt(rowIndex);
9842         
9843         var row = {
9844             tag : 'tr',
9845             cls : 'x-row-' + rowIndex,
9846             cn : []
9847         };
9848             
9849         var cellObjects = [];
9850         
9851         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9852             var config = cm.config[i];
9853             
9854             var renderer = cm.getRenderer(i);
9855             var value = '';
9856             var id = false;
9857             
9858             if(typeof(renderer) !== 'undefined'){
9859                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9860             }
9861             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9862             // and are rendered into the cells after the row is rendered - using the id for the element.
9863             
9864             if(typeof(value) === 'object'){
9865                 id = Roo.id();
9866                 cellObjects.push({
9867                     container : id,
9868                     cfg : value 
9869                 })
9870             }
9871             
9872             var rowcfg = {
9873                 record: d,
9874                 rowIndex : rowIndex,
9875                 colIndex : i,
9876                 rowClass : ''
9877             };
9878
9879             this.fireEvent('rowclass', this, rowcfg);
9880             
9881             var td = {
9882                 tag: 'td',
9883                 // this might end up displaying HTML?
9884                 // this is too messy... - better to only do it on columsn you know are going to be too long
9885                 //tooltip : (typeof(value) === 'object') ? '' : value,
9886                 cls : rowcfg.rowClass + ' x-col-' + i,
9887                 style: '',
9888                 html: (typeof(value) === 'object') ? '' : value
9889             };
9890             
9891             if (id) {
9892                 td.id = id;
9893             }
9894             
9895             if(typeof(config.colspan) != 'undefined'){
9896                 td.colspan = config.colspan;
9897             }
9898             
9899             
9900             
9901             if(typeof(config.align) != 'undefined' && config.align.length){
9902                 td.style += ' text-align:' + config.align + ';';
9903             }
9904             if(typeof(config.valign) != 'undefined' && config.valign.length){
9905                 td.style += ' vertical-align:' + config.valign + ';';
9906             }
9907             /*
9908             if(typeof(config.width) != 'undefined'){
9909                 td.style += ' width:' +  config.width + 'px;';
9910             }
9911             */
9912             
9913             if(typeof(config.cursor) != 'undefined'){
9914                 td.style += ' cursor:' +  config.cursor + ';';
9915             }
9916             
9917             if(typeof(config.cls) != 'undefined'){
9918                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9919             }
9920             if (this.responsive) {
9921                 ['xs','sm','md','lg'].map(function(size){
9922                     
9923                     if(typeof(config[size]) == 'undefined'){
9924                         return;
9925                     }
9926                     
9927                     
9928                       
9929                     if (!config[size]) { // 0 = hidden
9930                         // BS 4 '0' is treated as hide that column and below.
9931                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9932                         return;
9933                     }
9934                     
9935                     td.cls += ' col-' + size + '-' + config[size] + (
9936                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9937                     );
9938                      
9939     
9940                 });
9941             }
9942             row.cn.push(td);
9943            
9944         }
9945         
9946         row.cellObjects = cellObjects;
9947         
9948         return row;
9949           
9950     },
9951     
9952     
9953     
9954     onBeforeLoad : function()
9955     {
9956         
9957     },
9958      /**
9959      * Remove all rows
9960      */
9961     clear : function()
9962     {
9963         this.el.select('tbody', true).first().dom.innerHTML = '';
9964     },
9965     /**
9966      * Show or hide a row.
9967      * @param {Number} rowIndex to show or hide
9968      * @param {Boolean} state hide
9969      */
9970     setRowVisibility : function(rowIndex, state)
9971     {
9972         var bt = this.bodyEl.dom;
9973         
9974         var rows = this.el.select('tbody > tr', true).elements;
9975         
9976         if(typeof(rows[rowIndex]) == 'undefined'){
9977             return;
9978         }
9979         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9980         
9981     },
9982     
9983     
9984     getSelectionModel : function(){
9985         if(!this.selModel){
9986             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9987         }
9988         return this.selModel;
9989     },
9990     /*
9991      * Render the Roo.bootstrap object from renderder
9992      */
9993     renderCellObject : function(r)
9994     {
9995         var _this = this;
9996         
9997         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9998         
9999         var t = r.cfg.render(r.container);
10000         
10001         if(r.cfg.cn){
10002             Roo.each(r.cfg.cn, function(c){
10003                 var child = {
10004                     container: t.getChildContainer(),
10005                     cfg: c
10006                 };
10007                 _this.renderCellObject(child);
10008             })
10009         }
10010     },
10011     /**
10012      * get the Row Index from a dom element.
10013      * @param {Roo.Element} row The row to look for
10014      * @returns {Number} the row
10015      */
10016     getRowIndex : function(row)
10017     {
10018         var rowIndex = -1;
10019         
10020         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10021             if(el != row){
10022                 return;
10023             }
10024             
10025             rowIndex = index;
10026         });
10027         
10028         return rowIndex;
10029     },
10030     /**
10031      * get the header TH element for columnIndex
10032      * @param {Number} columnIndex
10033      * @returns {Roo.Element}
10034      */
10035     getHeaderIndex: function(colIndex)
10036     {
10037         var cols = this.headEl.select('th', true).elements;
10038         return cols[colIndex]; 
10039     },
10040     /**
10041      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10042      * @param {domElement} cell to look for
10043      * @returns {Number} the column
10044      */
10045     getCellIndex : function(cell)
10046     {
10047         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10048         if(id){
10049             return parseInt(id[1], 10);
10050         }
10051         return 0;
10052     },
10053      /**
10054      * Returns the grid's underlying element = used by panel.Grid
10055      * @return {Element} The element
10056      */
10057     getGridEl : function(){
10058         return this.el;
10059     },
10060      /**
10061      * Forces a resize - used by panel.Grid
10062      * @return {Element} The element
10063      */
10064     autoSize : function()
10065     {
10066         //var ctr = Roo.get(this.container.dom.parentElement);
10067         var ctr = Roo.get(this.el.dom);
10068         
10069         var thd = this.getGridEl().select('thead',true).first();
10070         var tbd = this.getGridEl().select('tbody', true).first();
10071         var tfd = this.getGridEl().select('tfoot', true).first();
10072         
10073         var cw = ctr.getWidth();
10074         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10075         
10076         if (tbd) {
10077             
10078             tbd.setWidth(ctr.getWidth());
10079             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10080             // this needs fixing for various usage - currently only hydra job advers I think..
10081             //tdb.setHeight(
10082             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10083             //); 
10084             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10085             cw -= barsize;
10086         }
10087         cw = Math.max(cw, this.totalWidth);
10088         this.getGridEl().select('tbody tr',true).setWidth(cw);
10089         this.initCSS();
10090         
10091         // resize 'expandable coloumn?
10092         
10093         return; // we doe not have a view in this design..
10094         
10095     },
10096     onBodyScroll: function()
10097     {
10098         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10099         if(this.headEl){
10100             this.headEl.setStyle({
10101                 'position' : 'relative',
10102                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10103             });
10104         }
10105         
10106         if(this.lazyLoad){
10107             
10108             var scrollHeight = this.bodyEl.dom.scrollHeight;
10109             
10110             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10111             
10112             var height = this.bodyEl.getHeight();
10113             
10114             if(scrollHeight - height == scrollTop) {
10115                 
10116                 var total = this.ds.getTotalCount();
10117                 
10118                 if(this.footer.cursor + this.footer.pageSize < total){
10119                     
10120                     this.footer.ds.load({
10121                         params : {
10122                             start : this.footer.cursor + this.footer.pageSize,
10123                             limit : this.footer.pageSize
10124                         },
10125                         add : true
10126                     });
10127                 }
10128             }
10129             
10130         }
10131     },
10132     onColumnSplitterMoved : function(i, diff)
10133     {
10134         this.userResized = true;
10135         
10136         var cm = this.colModel;
10137         
10138         var w = this.getHeaderIndex(i).getWidth() + diff;
10139         
10140         
10141         cm.setColumnWidth(i, w, true);
10142         this.initCSS();
10143         //var cid = cm.getColumnId(i); << not used in this version?
10144        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10145         
10146         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10147         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10148         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10149 */
10150         //this.updateSplitters();
10151         //this.layout(); << ??
10152         this.fireEvent("columnresize", i, w);
10153     },
10154     onHeaderChange : function()
10155     {
10156         var header = this.renderHeader();
10157         var table = this.el.select('table', true).first();
10158         
10159         this.headEl.remove();
10160         this.headEl = table.createChild(header, this.bodyEl, false);
10161         
10162         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10163             e.on('click', this.sort, this);
10164         }, this);
10165         
10166         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10167             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10168         }
10169         
10170     },
10171     
10172     onHiddenChange : function(colModel, colIndex, hidden)
10173     {
10174         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10175         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10176         
10177         this.CSS.updateRule(thSelector, "display", "");
10178         this.CSS.updateRule(tdSelector, "display", "");
10179         
10180         if(hidden){
10181             this.CSS.updateRule(thSelector, "display", "none");
10182             this.CSS.updateRule(tdSelector, "display", "none");
10183         }
10184         
10185         this.onHeaderChange();
10186         this.onLoad();
10187     },
10188     
10189     setColumnWidth: function(col_index, width)
10190     {
10191         // width = "md-2 xs-2..."
10192         if(!this.colModel.config[col_index]) {
10193             return;
10194         }
10195         
10196         var w = width.split(" ");
10197         
10198         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10199         
10200         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10201         
10202         
10203         for(var j = 0; j < w.length; j++) {
10204             
10205             if(!w[j]) {
10206                 continue;
10207             }
10208             
10209             var size_cls = w[j].split("-");
10210             
10211             if(!Number.isInteger(size_cls[1] * 1)) {
10212                 continue;
10213             }
10214             
10215             if(!this.colModel.config[col_index][size_cls[0]]) {
10216                 continue;
10217             }
10218             
10219             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10220                 continue;
10221             }
10222             
10223             h_row[0].classList.replace(
10224                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10225                 "col-"+size_cls[0]+"-"+size_cls[1]
10226             );
10227             
10228             for(var i = 0; i < rows.length; i++) {
10229                 
10230                 var size_cls = w[j].split("-");
10231                 
10232                 if(!Number.isInteger(size_cls[1] * 1)) {
10233                     continue;
10234                 }
10235                 
10236                 if(!this.colModel.config[col_index][size_cls[0]]) {
10237                     continue;
10238                 }
10239                 
10240                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10241                     continue;
10242                 }
10243                 
10244                 rows[i].classList.replace(
10245                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10246                     "col-"+size_cls[0]+"-"+size_cls[1]
10247                 );
10248             }
10249             
10250             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10251         }
10252     }
10253 });
10254
10255 // currently only used to find the split on drag.. 
10256 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10257
10258 /**
10259  * @depricated
10260 */
10261 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10262 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10263 /*
10264  * - LGPL
10265  *
10266  * table cell
10267  * 
10268  */
10269
10270 /**
10271  * @class Roo.bootstrap.TableCell
10272  * @extends Roo.bootstrap.Component
10273  * Bootstrap TableCell class
10274  * @cfg {String} html cell contain text
10275  * @cfg {String} cls cell class
10276  * @cfg {String} tag cell tag (td|th) default td
10277  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10278  * @cfg {String} align Aligns the content in a cell
10279  * @cfg {String} axis Categorizes cells
10280  * @cfg {String} bgcolor Specifies the background color of a cell
10281  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10282  * @cfg {Number} colspan Specifies the number of columns a cell should span
10283  * @cfg {String} headers Specifies one or more header cells a cell is related to
10284  * @cfg {Number} height Sets the height of a cell
10285  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10286  * @cfg {Number} rowspan Sets the number of rows a cell should span
10287  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10288  * @cfg {String} valign Vertical aligns the content in a cell
10289  * @cfg {Number} width Specifies the width of a cell
10290  * 
10291  * @constructor
10292  * Create a new TableCell
10293  * @param {Object} config The config object
10294  */
10295
10296 Roo.bootstrap.TableCell = function(config){
10297     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10298 };
10299
10300 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10301     
10302     html: false,
10303     cls: false,
10304     tag: false,
10305     abbr: false,
10306     align: false,
10307     axis: false,
10308     bgcolor: false,
10309     charoff: false,
10310     colspan: false,
10311     headers: false,
10312     height: false,
10313     nowrap: false,
10314     rowspan: false,
10315     scope: false,
10316     valign: false,
10317     width: false,
10318     
10319     
10320     getAutoCreate : function(){
10321         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10322         
10323         cfg = {
10324             tag: 'td'
10325         };
10326         
10327         if(this.tag){
10328             cfg.tag = this.tag;
10329         }
10330         
10331         if (this.html) {
10332             cfg.html=this.html
10333         }
10334         if (this.cls) {
10335             cfg.cls=this.cls
10336         }
10337         if (this.abbr) {
10338             cfg.abbr=this.abbr
10339         }
10340         if (this.align) {
10341             cfg.align=this.align
10342         }
10343         if (this.axis) {
10344             cfg.axis=this.axis
10345         }
10346         if (this.bgcolor) {
10347             cfg.bgcolor=this.bgcolor
10348         }
10349         if (this.charoff) {
10350             cfg.charoff=this.charoff
10351         }
10352         if (this.colspan) {
10353             cfg.colspan=this.colspan
10354         }
10355         if (this.headers) {
10356             cfg.headers=this.headers
10357         }
10358         if (this.height) {
10359             cfg.height=this.height
10360         }
10361         if (this.nowrap) {
10362             cfg.nowrap=this.nowrap
10363         }
10364         if (this.rowspan) {
10365             cfg.rowspan=this.rowspan
10366         }
10367         if (this.scope) {
10368             cfg.scope=this.scope
10369         }
10370         if (this.valign) {
10371             cfg.valign=this.valign
10372         }
10373         if (this.width) {
10374             cfg.width=this.width
10375         }
10376         
10377         
10378         return cfg;
10379     }
10380    
10381 });
10382
10383  
10384
10385  /*
10386  * - LGPL
10387  *
10388  * table row
10389  * 
10390  */
10391
10392 /**
10393  * @class Roo.bootstrap.TableRow
10394  * @extends Roo.bootstrap.Component
10395  * Bootstrap TableRow class
10396  * @cfg {String} cls row class
10397  * @cfg {String} align Aligns the content in a table row
10398  * @cfg {String} bgcolor Specifies a background color for a table row
10399  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10400  * @cfg {String} valign Vertical aligns the content in a table row
10401  * 
10402  * @constructor
10403  * Create a new TableRow
10404  * @param {Object} config The config object
10405  */
10406
10407 Roo.bootstrap.TableRow = function(config){
10408     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10409 };
10410
10411 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10412     
10413     cls: false,
10414     align: false,
10415     bgcolor: false,
10416     charoff: false,
10417     valign: false,
10418     
10419     getAutoCreate : function(){
10420         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10421         
10422         cfg = {
10423             tag: 'tr'
10424         };
10425             
10426         if(this.cls){
10427             cfg.cls = this.cls;
10428         }
10429         if(this.align){
10430             cfg.align = this.align;
10431         }
10432         if(this.bgcolor){
10433             cfg.bgcolor = this.bgcolor;
10434         }
10435         if(this.charoff){
10436             cfg.charoff = this.charoff;
10437         }
10438         if(this.valign){
10439             cfg.valign = this.valign;
10440         }
10441         
10442         return cfg;
10443     }
10444    
10445 });
10446
10447  
10448
10449  /*
10450  * - LGPL
10451  *
10452  * table body
10453  * 
10454  */
10455
10456 /**
10457  * @class Roo.bootstrap.TableBody
10458  * @extends Roo.bootstrap.Component
10459  * Bootstrap TableBody class
10460  * @cfg {String} cls element class
10461  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10462  * @cfg {String} align Aligns the content inside the element
10463  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10464  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10465  * 
10466  * @constructor
10467  * Create a new TableBody
10468  * @param {Object} config The config object
10469  */
10470
10471 Roo.bootstrap.TableBody = function(config){
10472     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10473 };
10474
10475 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10476     
10477     cls: false,
10478     tag: false,
10479     align: false,
10480     charoff: false,
10481     valign: false,
10482     
10483     getAutoCreate : function(){
10484         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10485         
10486         cfg = {
10487             tag: 'tbody'
10488         };
10489             
10490         if (this.cls) {
10491             cfg.cls=this.cls
10492         }
10493         if(this.tag){
10494             cfg.tag = this.tag;
10495         }
10496         
10497         if(this.align){
10498             cfg.align = this.align;
10499         }
10500         if(this.charoff){
10501             cfg.charoff = this.charoff;
10502         }
10503         if(this.valign){
10504             cfg.valign = this.valign;
10505         }
10506         
10507         return cfg;
10508     }
10509     
10510     
10511 //    initEvents : function()
10512 //    {
10513 //        
10514 //        if(!this.store){
10515 //            return;
10516 //        }
10517 //        
10518 //        this.store = Roo.factory(this.store, Roo.data);
10519 //        this.store.on('load', this.onLoad, this);
10520 //        
10521 //        this.store.load();
10522 //        
10523 //    },
10524 //    
10525 //    onLoad: function () 
10526 //    {   
10527 //        this.fireEvent('load', this);
10528 //    }
10529 //    
10530 //   
10531 });
10532
10533  
10534
10535  /*
10536  * Based on:
10537  * Ext JS Library 1.1.1
10538  * Copyright(c) 2006-2007, Ext JS, LLC.
10539  *
10540  * Originally Released Under LGPL - original licence link has changed is not relivant.
10541  *
10542  * Fork - LGPL
10543  * <script type="text/javascript">
10544  */
10545
10546 // as we use this in bootstrap.
10547 Roo.namespace('Roo.form');
10548  /**
10549  * @class Roo.form.Action
10550  * Internal Class used to handle form actions
10551  * @constructor
10552  * @param {Roo.form.BasicForm} el The form element or its id
10553  * @param {Object} config Configuration options
10554  */
10555
10556  
10557  
10558 // define the action interface
10559 Roo.form.Action = function(form, options){
10560     this.form = form;
10561     this.options = options || {};
10562 };
10563 /**
10564  * Client Validation Failed
10565  * @const 
10566  */
10567 Roo.form.Action.CLIENT_INVALID = 'client';
10568 /**
10569  * Server Validation Failed
10570  * @const 
10571  */
10572 Roo.form.Action.SERVER_INVALID = 'server';
10573  /**
10574  * Connect to Server Failed
10575  * @const 
10576  */
10577 Roo.form.Action.CONNECT_FAILURE = 'connect';
10578 /**
10579  * Reading Data from Server Failed
10580  * @const 
10581  */
10582 Roo.form.Action.LOAD_FAILURE = 'load';
10583
10584 Roo.form.Action.prototype = {
10585     type : 'default',
10586     failureType : undefined,
10587     response : undefined,
10588     result : undefined,
10589
10590     // interface method
10591     run : function(options){
10592
10593     },
10594
10595     // interface method
10596     success : function(response){
10597
10598     },
10599
10600     // interface method
10601     handleResponse : function(response){
10602
10603     },
10604
10605     // default connection failure
10606     failure : function(response){
10607         
10608         this.response = response;
10609         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10610         this.form.afterAction(this, false);
10611     },
10612
10613     processResponse : function(response){
10614         this.response = response;
10615         if(!response.responseText){
10616             return true;
10617         }
10618         this.result = this.handleResponse(response);
10619         return this.result;
10620     },
10621
10622     // utility functions used internally
10623     getUrl : function(appendParams){
10624         var url = this.options.url || this.form.url || this.form.el.dom.action;
10625         if(appendParams){
10626             var p = this.getParams();
10627             if(p){
10628                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10629             }
10630         }
10631         return url;
10632     },
10633
10634     getMethod : function(){
10635         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10636     },
10637
10638     getParams : function(){
10639         var bp = this.form.baseParams;
10640         var p = this.options.params;
10641         if(p){
10642             if(typeof p == "object"){
10643                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10644             }else if(typeof p == 'string' && bp){
10645                 p += '&' + Roo.urlEncode(bp);
10646             }
10647         }else if(bp){
10648             p = Roo.urlEncode(bp);
10649         }
10650         return p;
10651     },
10652
10653     createCallback : function(){
10654         return {
10655             success: this.success,
10656             failure: this.failure,
10657             scope: this,
10658             timeout: (this.form.timeout*1000),
10659             upload: this.form.fileUpload ? this.success : undefined
10660         };
10661     }
10662 };
10663
10664 Roo.form.Action.Submit = function(form, options){
10665     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10666 };
10667
10668 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10669     type : 'submit',
10670
10671     haveProgress : false,
10672     uploadComplete : false,
10673     
10674     // uploadProgress indicator.
10675     uploadProgress : function()
10676     {
10677         if (!this.form.progressUrl) {
10678             return;
10679         }
10680         
10681         if (!this.haveProgress) {
10682             Roo.MessageBox.progress("Uploading", "Uploading");
10683         }
10684         if (this.uploadComplete) {
10685            Roo.MessageBox.hide();
10686            return;
10687         }
10688         
10689         this.haveProgress = true;
10690    
10691         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10692         
10693         var c = new Roo.data.Connection();
10694         c.request({
10695             url : this.form.progressUrl,
10696             params: {
10697                 id : uid
10698             },
10699             method: 'GET',
10700             success : function(req){
10701                //console.log(data);
10702                 var rdata = false;
10703                 var edata;
10704                 try  {
10705                    rdata = Roo.decode(req.responseText)
10706                 } catch (e) {
10707                     Roo.log("Invalid data from server..");
10708                     Roo.log(edata);
10709                     return;
10710                 }
10711                 if (!rdata || !rdata.success) {
10712                     Roo.log(rdata);
10713                     Roo.MessageBox.alert(Roo.encode(rdata));
10714                     return;
10715                 }
10716                 var data = rdata.data;
10717                 
10718                 if (this.uploadComplete) {
10719                    Roo.MessageBox.hide();
10720                    return;
10721                 }
10722                    
10723                 if (data){
10724                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10725                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10726                     );
10727                 }
10728                 this.uploadProgress.defer(2000,this);
10729             },
10730        
10731             failure: function(data) {
10732                 Roo.log('progress url failed ');
10733                 Roo.log(data);
10734             },
10735             scope : this
10736         });
10737            
10738     },
10739     
10740     
10741     run : function()
10742     {
10743         // run get Values on the form, so it syncs any secondary forms.
10744         this.form.getValues();
10745         
10746         var o = this.options;
10747         var method = this.getMethod();
10748         var isPost = method == 'POST';
10749         if(o.clientValidation === false || this.form.isValid()){
10750             
10751             if (this.form.progressUrl) {
10752                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10753                     (new Date() * 1) + '' + Math.random());
10754                     
10755             } 
10756             
10757             
10758             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10759                 form:this.form.el.dom,
10760                 url:this.getUrl(!isPost),
10761                 method: method,
10762                 params:isPost ? this.getParams() : null,
10763                 isUpload: this.form.fileUpload,
10764                 formData : this.form.formData
10765             }));
10766             
10767             this.uploadProgress();
10768
10769         }else if (o.clientValidation !== false){ // client validation failed
10770             this.failureType = Roo.form.Action.CLIENT_INVALID;
10771             this.form.afterAction(this, false);
10772         }
10773     },
10774
10775     success : function(response)
10776     {
10777         this.uploadComplete= true;
10778         if (this.haveProgress) {
10779             Roo.MessageBox.hide();
10780         }
10781         
10782         
10783         var result = this.processResponse(response);
10784         if(result === true || result.success){
10785             this.form.afterAction(this, true);
10786             return;
10787         }
10788         if(result.errors){
10789             this.form.markInvalid(result.errors);
10790             this.failureType = Roo.form.Action.SERVER_INVALID;
10791         }
10792         this.form.afterAction(this, false);
10793     },
10794     failure : function(response)
10795     {
10796         this.uploadComplete= true;
10797         if (this.haveProgress) {
10798             Roo.MessageBox.hide();
10799         }
10800         
10801         this.response = response;
10802         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10803         this.form.afterAction(this, false);
10804     },
10805     
10806     handleResponse : function(response){
10807         if(this.form.errorReader){
10808             var rs = this.form.errorReader.read(response);
10809             var errors = [];
10810             if(rs.records){
10811                 for(var i = 0, len = rs.records.length; i < len; i++) {
10812                     var r = rs.records[i];
10813                     errors[i] = r.data;
10814                 }
10815             }
10816             if(errors.length < 1){
10817                 errors = null;
10818             }
10819             return {
10820                 success : rs.success,
10821                 errors : errors
10822             };
10823         }
10824         var ret = false;
10825         try {
10826             ret = Roo.decode(response.responseText);
10827         } catch (e) {
10828             ret = {
10829                 success: false,
10830                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10831                 errors : []
10832             };
10833         }
10834         return ret;
10835         
10836     }
10837 });
10838
10839
10840 Roo.form.Action.Load = function(form, options){
10841     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10842     this.reader = this.form.reader;
10843 };
10844
10845 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10846     type : 'load',
10847
10848     run : function(){
10849         
10850         Roo.Ajax.request(Roo.apply(
10851                 this.createCallback(), {
10852                     method:this.getMethod(),
10853                     url:this.getUrl(false),
10854                     params:this.getParams()
10855         }));
10856     },
10857
10858     success : function(response){
10859         
10860         var result = this.processResponse(response);
10861         if(result === true || !result.success || !result.data){
10862             this.failureType = Roo.form.Action.LOAD_FAILURE;
10863             this.form.afterAction(this, false);
10864             return;
10865         }
10866         this.form.clearInvalid();
10867         this.form.setValues(result.data);
10868         this.form.afterAction(this, true);
10869     },
10870
10871     handleResponse : function(response){
10872         if(this.form.reader){
10873             var rs = this.form.reader.read(response);
10874             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10875             return {
10876                 success : rs.success,
10877                 data : data
10878             };
10879         }
10880         return Roo.decode(response.responseText);
10881     }
10882 });
10883
10884 Roo.form.Action.ACTION_TYPES = {
10885     'load' : Roo.form.Action.Load,
10886     'submit' : Roo.form.Action.Submit
10887 };/*
10888  * - LGPL
10889  *
10890  * form
10891  *
10892  */
10893
10894 /**
10895  * @class Roo.bootstrap.Form
10896  * @extends Roo.bootstrap.Component
10897  * Bootstrap Form class
10898  * @cfg {String} method  GET | POST (default POST)
10899  * @cfg {String} labelAlign top | left (default top)
10900  * @cfg {String} align left  | right - for navbars
10901  * @cfg {Boolean} loadMask load mask when submit (default true)
10902
10903  *
10904  * @constructor
10905  * Create a new Form
10906  * @param {Object} config The config object
10907  */
10908
10909
10910 Roo.bootstrap.Form = function(config){
10911     
10912     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10913     
10914     Roo.bootstrap.Form.popover.apply();
10915     
10916     this.addEvents({
10917         /**
10918          * @event clientvalidation
10919          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10920          * @param {Form} this
10921          * @param {Boolean} valid true if the form has passed client-side validation
10922          */
10923         clientvalidation: true,
10924         /**
10925          * @event beforeaction
10926          * Fires before any action is performed. Return false to cancel the action.
10927          * @param {Form} this
10928          * @param {Action} action The action to be performed
10929          */
10930         beforeaction: true,
10931         /**
10932          * @event actionfailed
10933          * Fires when an action fails.
10934          * @param {Form} this
10935          * @param {Action} action The action that failed
10936          */
10937         actionfailed : true,
10938         /**
10939          * @event actioncomplete
10940          * Fires when an action is completed.
10941          * @param {Form} this
10942          * @param {Action} action The action that completed
10943          */
10944         actioncomplete : true
10945     });
10946 };
10947
10948 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10949
10950      /**
10951      * @cfg {String} method
10952      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10953      */
10954     method : 'POST',
10955     /**
10956      * @cfg {String} url
10957      * The URL to use for form actions if one isn't supplied in the action options.
10958      */
10959     /**
10960      * @cfg {Boolean} fileUpload
10961      * Set to true if this form is a file upload.
10962      */
10963
10964     /**
10965      * @cfg {Object} baseParams
10966      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10967      */
10968
10969     /**
10970      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10971      */
10972     timeout: 30,
10973     /**
10974      * @cfg {Sting} align (left|right) for navbar forms
10975      */
10976     align : 'left',
10977
10978     // private
10979     activeAction : null,
10980
10981     /**
10982      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10983      * element by passing it or its id or mask the form itself by passing in true.
10984      * @type Mixed
10985      */
10986     waitMsgTarget : false,
10987
10988     loadMask : true,
10989     
10990     /**
10991      * @cfg {Boolean} errorMask (true|false) default false
10992      */
10993     errorMask : false,
10994     
10995     /**
10996      * @cfg {Number} maskOffset Default 100
10997      */
10998     maskOffset : 100,
10999     
11000     /**
11001      * @cfg {Boolean} maskBody
11002      */
11003     maskBody : false,
11004
11005     getAutoCreate : function(){
11006
11007         var cfg = {
11008             tag: 'form',
11009             method : this.method || 'POST',
11010             id : this.id || Roo.id(),
11011             cls : ''
11012         };
11013         if (this.parent().xtype.match(/^Nav/)) {
11014             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11015
11016         }
11017
11018         if (this.labelAlign == 'left' ) {
11019             cfg.cls += ' form-horizontal';
11020         }
11021
11022
11023         return cfg;
11024     },
11025     initEvents : function()
11026     {
11027         this.el.on('submit', this.onSubmit, this);
11028         // this was added as random key presses on the form where triggering form submit.
11029         this.el.on('keypress', function(e) {
11030             if (e.getCharCode() != 13) {
11031                 return true;
11032             }
11033             // we might need to allow it for textareas.. and some other items.
11034             // check e.getTarget().
11035
11036             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11037                 return true;
11038             }
11039
11040             Roo.log("keypress blocked");
11041
11042             e.preventDefault();
11043             return false;
11044         });
11045         
11046     },
11047     // private
11048     onSubmit : function(e){
11049         e.stopEvent();
11050     },
11051
11052      /**
11053      * Returns true if client-side validation on the form is successful.
11054      * @return Boolean
11055      */
11056     isValid : function(){
11057         var items = this.getItems();
11058         var valid = true;
11059         var target = false;
11060         
11061         items.each(function(f){
11062             
11063             if(f.validate()){
11064                 return;
11065             }
11066             
11067             Roo.log('invalid field: ' + f.name);
11068             
11069             valid = false;
11070
11071             if(!target && f.el.isVisible(true)){
11072                 target = f;
11073             }
11074            
11075         });
11076         
11077         if(this.errorMask && !valid){
11078             Roo.bootstrap.Form.popover.mask(this, target);
11079         }
11080         
11081         return valid;
11082     },
11083     
11084     /**
11085      * Returns true if any fields in this form have changed since their original load.
11086      * @return Boolean
11087      */
11088     isDirty : function(){
11089         var dirty = false;
11090         var items = this.getItems();
11091         items.each(function(f){
11092            if(f.isDirty()){
11093                dirty = true;
11094                return false;
11095            }
11096            return true;
11097         });
11098         return dirty;
11099     },
11100      /**
11101      * Performs a predefined action (submit or load) or custom actions you define on this form.
11102      * @param {String} actionName The name of the action type
11103      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11104      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11105      * accept other config options):
11106      * <pre>
11107 Property          Type             Description
11108 ----------------  ---------------  ----------------------------------------------------------------------------------
11109 url               String           The url for the action (defaults to the form's url)
11110 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11111 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11112 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11113                                    validate the form on the client (defaults to false)
11114      * </pre>
11115      * @return {BasicForm} this
11116      */
11117     doAction : function(action, options){
11118         if(typeof action == 'string'){
11119             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11120         }
11121         if(this.fireEvent('beforeaction', this, action) !== false){
11122             this.beforeAction(action);
11123             action.run.defer(100, action);
11124         }
11125         return this;
11126     },
11127
11128     // private
11129     beforeAction : function(action){
11130         var o = action.options;
11131         
11132         if(this.loadMask){
11133             
11134             if(this.maskBody){
11135                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11136             } else {
11137                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11138             }
11139         }
11140         // not really supported yet.. ??
11141
11142         //if(this.waitMsgTarget === true){
11143         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11144         //}else if(this.waitMsgTarget){
11145         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11146         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11147         //}else {
11148         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11149        // }
11150
11151     },
11152
11153     // private
11154     afterAction : function(action, success){
11155         this.activeAction = null;
11156         var o = action.options;
11157
11158         if(this.loadMask){
11159             
11160             if(this.maskBody){
11161                 Roo.get(document.body).unmask();
11162             } else {
11163                 this.el.unmask();
11164             }
11165         }
11166         
11167         //if(this.waitMsgTarget === true){
11168 //            this.el.unmask();
11169         //}else if(this.waitMsgTarget){
11170         //    this.waitMsgTarget.unmask();
11171         //}else{
11172         //    Roo.MessageBox.updateProgress(1);
11173         //    Roo.MessageBox.hide();
11174        // }
11175         //
11176         if(success){
11177             if(o.reset){
11178                 this.reset();
11179             }
11180             Roo.callback(o.success, o.scope, [this, action]);
11181             this.fireEvent('actioncomplete', this, action);
11182
11183         }else{
11184
11185             // failure condition..
11186             // we have a scenario where updates need confirming.
11187             // eg. if a locking scenario exists..
11188             // we look for { errors : { needs_confirm : true }} in the response.
11189             if (
11190                 (typeof(action.result) != 'undefined')  &&
11191                 (typeof(action.result.errors) != 'undefined')  &&
11192                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11193            ){
11194                 var _t = this;
11195                 Roo.log("not supported yet");
11196                  /*
11197
11198                 Roo.MessageBox.confirm(
11199                     "Change requires confirmation",
11200                     action.result.errorMsg,
11201                     function(r) {
11202                         if (r != 'yes') {
11203                             return;
11204                         }
11205                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11206                     }
11207
11208                 );
11209                 */
11210
11211
11212                 return;
11213             }
11214
11215             Roo.callback(o.failure, o.scope, [this, action]);
11216             // show an error message if no failed handler is set..
11217             if (!this.hasListener('actionfailed')) {
11218                 Roo.log("need to add dialog support");
11219                 /*
11220                 Roo.MessageBox.alert("Error",
11221                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11222                         action.result.errorMsg :
11223                         "Saving Failed, please check your entries or try again"
11224                 );
11225                 */
11226             }
11227
11228             this.fireEvent('actionfailed', this, action);
11229         }
11230
11231     },
11232     /**
11233      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11234      * @param {String} id The value to search for
11235      * @return Field
11236      */
11237     findField : function(id){
11238         var items = this.getItems();
11239         var field = items.get(id);
11240         if(!field){
11241              items.each(function(f){
11242                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11243                     field = f;
11244                     return false;
11245                 }
11246                 return true;
11247             });
11248         }
11249         return field || null;
11250     },
11251      /**
11252      * Mark fields in this form invalid in bulk.
11253      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11254      * @return {BasicForm} this
11255      */
11256     markInvalid : function(errors){
11257         if(errors instanceof Array){
11258             for(var i = 0, len = errors.length; i < len; i++){
11259                 var fieldError = errors[i];
11260                 var f = this.findField(fieldError.id);
11261                 if(f){
11262                     f.markInvalid(fieldError.msg);
11263                 }
11264             }
11265         }else{
11266             var field, id;
11267             for(id in errors){
11268                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11269                     field.markInvalid(errors[id]);
11270                 }
11271             }
11272         }
11273         //Roo.each(this.childForms || [], function (f) {
11274         //    f.markInvalid(errors);
11275         //});
11276
11277         return this;
11278     },
11279
11280     /**
11281      * Set values for fields in this form in bulk.
11282      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11283      * @return {BasicForm} this
11284      */
11285     setValues : function(values){
11286         if(values instanceof Array){ // array of objects
11287             for(var i = 0, len = values.length; i < len; i++){
11288                 var v = values[i];
11289                 var f = this.findField(v.id);
11290                 if(f){
11291                     f.setValue(v.value);
11292                     if(this.trackResetOnLoad){
11293                         f.originalValue = f.getValue();
11294                     }
11295                 }
11296             }
11297         }else{ // object hash
11298             var field, id;
11299             for(id in values){
11300                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11301
11302                     if (field.setFromData &&
11303                         field.valueField &&
11304                         field.displayField &&
11305                         // combos' with local stores can
11306                         // be queried via setValue()
11307                         // to set their value..
11308                         (field.store && !field.store.isLocal)
11309                         ) {
11310                         // it's a combo
11311                         var sd = { };
11312                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11313                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11314                         field.setFromData(sd);
11315
11316                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11317                         
11318                         field.setFromData(values);
11319                         
11320                     } else {
11321                         field.setValue(values[id]);
11322                     }
11323
11324
11325                     if(this.trackResetOnLoad){
11326                         field.originalValue = field.getValue();
11327                     }
11328                 }
11329             }
11330         }
11331
11332         //Roo.each(this.childForms || [], function (f) {
11333         //    f.setValues(values);
11334         //});
11335
11336         return this;
11337     },
11338
11339     /**
11340      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11341      * they are returned as an array.
11342      * @param {Boolean} asString
11343      * @return {Object}
11344      */
11345     getValues : function(asString){
11346         //if (this.childForms) {
11347             // copy values from the child forms
11348         //    Roo.each(this.childForms, function (f) {
11349         //        this.setValues(f.getValues());
11350         //    }, this);
11351         //}
11352
11353
11354
11355         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11356         if(asString === true){
11357             return fs;
11358         }
11359         return Roo.urlDecode(fs);
11360     },
11361
11362     /**
11363      * Returns the fields in this form as an object with key/value pairs.
11364      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11365      * @return {Object}
11366      */
11367     getFieldValues : function(with_hidden)
11368     {
11369         var items = this.getItems();
11370         var ret = {};
11371         items.each(function(f){
11372             
11373             if (!f.getName()) {
11374                 return;
11375             }
11376             
11377             var v = f.getValue();
11378             
11379             if (f.inputType =='radio') {
11380                 if (typeof(ret[f.getName()]) == 'undefined') {
11381                     ret[f.getName()] = ''; // empty..
11382                 }
11383
11384                 if (!f.el.dom.checked) {
11385                     return;
11386
11387                 }
11388                 v = f.el.dom.value;
11389
11390             }
11391             
11392             if(f.xtype == 'MoneyField'){
11393                 ret[f.currencyName] = f.getCurrency();
11394             }
11395
11396             // not sure if this supported any more..
11397             if ((typeof(v) == 'object') && f.getRawValue) {
11398                 v = f.getRawValue() ; // dates..
11399             }
11400             // combo boxes where name != hiddenName...
11401             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11402                 ret[f.name] = f.getRawValue();
11403             }
11404             ret[f.getName()] = v;
11405         });
11406
11407         return ret;
11408     },
11409
11410     /**
11411      * Clears all invalid messages in this form.
11412      * @return {BasicForm} this
11413      */
11414     clearInvalid : function(){
11415         var items = this.getItems();
11416
11417         items.each(function(f){
11418            f.clearInvalid();
11419         });
11420
11421         return this;
11422     },
11423
11424     /**
11425      * Resets this form.
11426      * @return {BasicForm} this
11427      */
11428     reset : function(){
11429         var items = this.getItems();
11430         items.each(function(f){
11431             f.reset();
11432         });
11433
11434         Roo.each(this.childForms || [], function (f) {
11435             f.reset();
11436         });
11437
11438
11439         return this;
11440     },
11441     
11442     getItems : function()
11443     {
11444         var r=new Roo.util.MixedCollection(false, function(o){
11445             return o.id || (o.id = Roo.id());
11446         });
11447         var iter = function(el) {
11448             if (el.inputEl) {
11449                 r.add(el);
11450             }
11451             if (!el.items) {
11452                 return;
11453             }
11454             Roo.each(el.items,function(e) {
11455                 iter(e);
11456             });
11457         };
11458
11459         iter(this);
11460         return r;
11461     },
11462     
11463     hideFields : function(items)
11464     {
11465         Roo.each(items, function(i){
11466             
11467             var f = this.findField(i);
11468             
11469             if(!f){
11470                 return;
11471             }
11472             
11473             f.hide();
11474             
11475         }, this);
11476     },
11477     
11478     showFields : function(items)
11479     {
11480         Roo.each(items, function(i){
11481             
11482             var f = this.findField(i);
11483             
11484             if(!f){
11485                 return;
11486             }
11487             
11488             f.show();
11489             
11490         }, this);
11491     }
11492
11493 });
11494
11495 Roo.apply(Roo.bootstrap.Form, {
11496     
11497     popover : {
11498         
11499         padding : 5,
11500         
11501         isApplied : false,
11502         
11503         isMasked : false,
11504         
11505         form : false,
11506         
11507         target : false,
11508         
11509         toolTip : false,
11510         
11511         intervalID : false,
11512         
11513         maskEl : false,
11514         
11515         apply : function()
11516         {
11517             if(this.isApplied){
11518                 return;
11519             }
11520             
11521             this.maskEl = {
11522                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11523                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11524                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11525                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11526             };
11527             
11528             this.maskEl.top.enableDisplayMode("block");
11529             this.maskEl.left.enableDisplayMode("block");
11530             this.maskEl.bottom.enableDisplayMode("block");
11531             this.maskEl.right.enableDisplayMode("block");
11532             
11533             this.toolTip = new Roo.bootstrap.Tooltip({
11534                 cls : 'roo-form-error-popover',
11535                 alignment : {
11536                     'left' : ['r-l', [-2,0], 'right'],
11537                     'right' : ['l-r', [2,0], 'left'],
11538                     'bottom' : ['tl-bl', [0,2], 'top'],
11539                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11540                 }
11541             });
11542             
11543             this.toolTip.render(Roo.get(document.body));
11544
11545             this.toolTip.el.enableDisplayMode("block");
11546             
11547             Roo.get(document.body).on('click', function(){
11548                 this.unmask();
11549             }, this);
11550             
11551             Roo.get(document.body).on('touchstart', function(){
11552                 this.unmask();
11553             }, this);
11554             
11555             this.isApplied = true
11556         },
11557         
11558         mask : function(form, target)
11559         {
11560             this.form = form;
11561             
11562             this.target = target;
11563             
11564             if(!this.form.errorMask || !target.el){
11565                 return;
11566             }
11567             
11568             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11569             
11570             Roo.log(scrollable);
11571             
11572             var ot = this.target.el.calcOffsetsTo(scrollable);
11573             
11574             var scrollTo = ot[1] - this.form.maskOffset;
11575             
11576             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11577             
11578             scrollable.scrollTo('top', scrollTo);
11579             
11580             var box = this.target.el.getBox();
11581             Roo.log(box);
11582             var zIndex = Roo.bootstrap.Modal.zIndex++;
11583
11584             
11585             this.maskEl.top.setStyle('position', 'absolute');
11586             this.maskEl.top.setStyle('z-index', zIndex);
11587             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11588             this.maskEl.top.setLeft(0);
11589             this.maskEl.top.setTop(0);
11590             this.maskEl.top.show();
11591             
11592             this.maskEl.left.setStyle('position', 'absolute');
11593             this.maskEl.left.setStyle('z-index', zIndex);
11594             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11595             this.maskEl.left.setLeft(0);
11596             this.maskEl.left.setTop(box.y - this.padding);
11597             this.maskEl.left.show();
11598
11599             this.maskEl.bottom.setStyle('position', 'absolute');
11600             this.maskEl.bottom.setStyle('z-index', zIndex);
11601             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11602             this.maskEl.bottom.setLeft(0);
11603             this.maskEl.bottom.setTop(box.bottom + this.padding);
11604             this.maskEl.bottom.show();
11605
11606             this.maskEl.right.setStyle('position', 'absolute');
11607             this.maskEl.right.setStyle('z-index', zIndex);
11608             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11609             this.maskEl.right.setLeft(box.right + this.padding);
11610             this.maskEl.right.setTop(box.y - this.padding);
11611             this.maskEl.right.show();
11612
11613             this.toolTip.bindEl = this.target.el;
11614
11615             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11616
11617             var tip = this.target.blankText;
11618
11619             if(this.target.getValue() !== '' ) {
11620                 
11621                 if (this.target.invalidText.length) {
11622                     tip = this.target.invalidText;
11623                 } else if (this.target.regexText.length){
11624                     tip = this.target.regexText;
11625                 }
11626             }
11627
11628             this.toolTip.show(tip);
11629
11630             this.intervalID = window.setInterval(function() {
11631                 Roo.bootstrap.Form.popover.unmask();
11632             }, 10000);
11633
11634             window.onwheel = function(){ return false;};
11635             
11636             (function(){ this.isMasked = true; }).defer(500, this);
11637             
11638         },
11639         
11640         unmask : function()
11641         {
11642             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11643                 return;
11644             }
11645             
11646             this.maskEl.top.setStyle('position', 'absolute');
11647             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11648             this.maskEl.top.hide();
11649
11650             this.maskEl.left.setStyle('position', 'absolute');
11651             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11652             this.maskEl.left.hide();
11653
11654             this.maskEl.bottom.setStyle('position', 'absolute');
11655             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11656             this.maskEl.bottom.hide();
11657
11658             this.maskEl.right.setStyle('position', 'absolute');
11659             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11660             this.maskEl.right.hide();
11661             
11662             this.toolTip.hide();
11663             
11664             this.toolTip.el.hide();
11665             
11666             window.onwheel = function(){ return true;};
11667             
11668             if(this.intervalID){
11669                 window.clearInterval(this.intervalID);
11670                 this.intervalID = false;
11671             }
11672             
11673             this.isMasked = false;
11674             
11675         }
11676         
11677     }
11678     
11679 });
11680
11681 /*
11682  * Based on:
11683  * Ext JS Library 1.1.1
11684  * Copyright(c) 2006-2007, Ext JS, LLC.
11685  *
11686  * Originally Released Under LGPL - original licence link has changed is not relivant.
11687  *
11688  * Fork - LGPL
11689  * <script type="text/javascript">
11690  */
11691 /**
11692  * @class Roo.form.VTypes
11693  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11694  * @singleton
11695  */
11696 Roo.form.VTypes = function(){
11697     // closure these in so they are only created once.
11698     var alpha = /^[a-zA-Z_]+$/;
11699     var alphanum = /^[a-zA-Z0-9_]+$/;
11700     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11701     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11702
11703     // All these messages and functions are configurable
11704     return {
11705         /**
11706          * The function used to validate email addresses
11707          * @param {String} value The email address
11708          */
11709         'email' : function(v){
11710             return email.test(v);
11711         },
11712         /**
11713          * The error text to display when the email validation function returns false
11714          * @type String
11715          */
11716         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11717         /**
11718          * The keystroke filter mask to be applied on email input
11719          * @type RegExp
11720          */
11721         'emailMask' : /[a-z0-9_\.\-@]/i,
11722
11723         /**
11724          * The function used to validate URLs
11725          * @param {String} value The URL
11726          */
11727         'url' : function(v){
11728             return url.test(v);
11729         },
11730         /**
11731          * The error text to display when the url validation function returns false
11732          * @type String
11733          */
11734         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11735         
11736         /**
11737          * The function used to validate alpha values
11738          * @param {String} value The value
11739          */
11740         'alpha' : function(v){
11741             return alpha.test(v);
11742         },
11743         /**
11744          * The error text to display when the alpha validation function returns false
11745          * @type String
11746          */
11747         'alphaText' : 'This field should only contain letters and _',
11748         /**
11749          * The keystroke filter mask to be applied on alpha input
11750          * @type RegExp
11751          */
11752         'alphaMask' : /[a-z_]/i,
11753
11754         /**
11755          * The function used to validate alphanumeric values
11756          * @param {String} value The value
11757          */
11758         'alphanum' : function(v){
11759             return alphanum.test(v);
11760         },
11761         /**
11762          * The error text to display when the alphanumeric validation function returns false
11763          * @type String
11764          */
11765         'alphanumText' : 'This field should only contain letters, numbers and _',
11766         /**
11767          * The keystroke filter mask to be applied on alphanumeric input
11768          * @type RegExp
11769          */
11770         'alphanumMask' : /[a-z0-9_]/i
11771     };
11772 }();/*
11773  * - LGPL
11774  *
11775  * Input
11776  * 
11777  */
11778
11779 /**
11780  * @class Roo.bootstrap.Input
11781  * @extends Roo.bootstrap.Component
11782  * Bootstrap Input class
11783  * @cfg {Boolean} disabled is it disabled
11784  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11785  * @cfg {String} name name of the input
11786  * @cfg {string} fieldLabel - the label associated
11787  * @cfg {string} placeholder - placeholder to put in text.
11788  * @cfg {string}  before - input group add on before
11789  * @cfg {string} after - input group add on after
11790  * @cfg {string} size - (lg|sm) or leave empty..
11791  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11792  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11793  * @cfg {Number} md colspan out of 12 for computer-sized screens
11794  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11795  * @cfg {string} value default value of the input
11796  * @cfg {Number} labelWidth set the width of label 
11797  * @cfg {Number} labellg set the width of label (1-12)
11798  * @cfg {Number} labelmd set the width of label (1-12)
11799  * @cfg {Number} labelsm set the width of label (1-12)
11800  * @cfg {Number} labelxs set the width of label (1-12)
11801  * @cfg {String} labelAlign (top|left)
11802  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11803  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11804  * @cfg {String} indicatorpos (left|right) default left
11805  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11806  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11807  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11808
11809  * @cfg {String} align (left|center|right) Default left
11810  * @cfg {Boolean} forceFeedback (true|false) Default false
11811  * 
11812  * @constructor
11813  * Create a new Input
11814  * @param {Object} config The config object
11815  */
11816
11817 Roo.bootstrap.Input = function(config){
11818     
11819     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11820     
11821     this.addEvents({
11822         /**
11823          * @event focus
11824          * Fires when this field receives input focus.
11825          * @param {Roo.form.Field} this
11826          */
11827         focus : true,
11828         /**
11829          * @event blur
11830          * Fires when this field loses input focus.
11831          * @param {Roo.form.Field} this
11832          */
11833         blur : true,
11834         /**
11835          * @event specialkey
11836          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11837          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11838          * @param {Roo.form.Field} this
11839          * @param {Roo.EventObject} e The event object
11840          */
11841         specialkey : true,
11842         /**
11843          * @event change
11844          * Fires just before the field blurs if the field value has changed.
11845          * @param {Roo.form.Field} this
11846          * @param {Mixed} newValue The new value
11847          * @param {Mixed} oldValue The original value
11848          */
11849         change : true,
11850         /**
11851          * @event invalid
11852          * Fires after the field has been marked as invalid.
11853          * @param {Roo.form.Field} this
11854          * @param {String} msg The validation message
11855          */
11856         invalid : true,
11857         /**
11858          * @event valid
11859          * Fires after the field has been validated with no errors.
11860          * @param {Roo.form.Field} this
11861          */
11862         valid : true,
11863          /**
11864          * @event keyup
11865          * Fires after the key up
11866          * @param {Roo.form.Field} this
11867          * @param {Roo.EventObject}  e The event Object
11868          */
11869         keyup : true,
11870         /**
11871          * @event paste
11872          * Fires after the user pastes into input
11873          * @param {Roo.form.Field} this
11874          * @param {Roo.EventObject}  e The event Object
11875          */
11876         paste : true
11877     });
11878 };
11879
11880 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11881      /**
11882      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11883       automatic validation (defaults to "keyup").
11884      */
11885     validationEvent : "keyup",
11886      /**
11887      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11888      */
11889     validateOnBlur : true,
11890     /**
11891      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11892      */
11893     validationDelay : 250,
11894      /**
11895      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11896      */
11897     focusClass : "x-form-focus",  // not needed???
11898     
11899        
11900     /**
11901      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11902      */
11903     invalidClass : "has-warning",
11904     
11905     /**
11906      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11907      */
11908     validClass : "has-success",
11909     
11910     /**
11911      * @cfg {Boolean} hasFeedback (true|false) default true
11912      */
11913     hasFeedback : true,
11914     
11915     /**
11916      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11917      */
11918     invalidFeedbackClass : "glyphicon-warning-sign",
11919     
11920     /**
11921      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11922      */
11923     validFeedbackClass : "glyphicon-ok",
11924     
11925     /**
11926      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11927      */
11928     selectOnFocus : false,
11929     
11930      /**
11931      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11932      */
11933     maskRe : null,
11934        /**
11935      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11936      */
11937     vtype : null,
11938     
11939       /**
11940      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11941      */
11942     disableKeyFilter : false,
11943     
11944        /**
11945      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11946      */
11947     disabled : false,
11948      /**
11949      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11950      */
11951     allowBlank : true,
11952     /**
11953      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11954      */
11955     blankText : "Please complete this mandatory field",
11956     
11957      /**
11958      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11959      */
11960     minLength : 0,
11961     /**
11962      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11963      */
11964     maxLength : Number.MAX_VALUE,
11965     /**
11966      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11967      */
11968     minLengthText : "The minimum length for this field is {0}",
11969     /**
11970      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11971      */
11972     maxLengthText : "The maximum length for this field is {0}",
11973   
11974     
11975     /**
11976      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11977      * If available, this function will be called only after the basic validators all return true, and will be passed the
11978      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11979      */
11980     validator : null,
11981     /**
11982      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11983      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11984      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11985      */
11986     regex : null,
11987     /**
11988      * @cfg {String} regexText -- Depricated - use Invalid Text
11989      */
11990     regexText : "",
11991     
11992     /**
11993      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11994      */
11995     invalidText : "",
11996     
11997     
11998     
11999     autocomplete: false,
12000     
12001     
12002     fieldLabel : '',
12003     inputType : 'text',
12004     
12005     name : false,
12006     placeholder: false,
12007     before : false,
12008     after : false,
12009     size : false,
12010     hasFocus : false,
12011     preventMark: false,
12012     isFormField : true,
12013     value : '',
12014     labelWidth : 2,
12015     labelAlign : false,
12016     readOnly : false,
12017     align : false,
12018     formatedValue : false,
12019     forceFeedback : false,
12020     
12021     indicatorpos : 'left',
12022     
12023     labellg : 0,
12024     labelmd : 0,
12025     labelsm : 0,
12026     labelxs : 0,
12027     
12028     capture : '',
12029     accept : '',
12030     
12031     parentLabelAlign : function()
12032     {
12033         var parent = this;
12034         while (parent.parent()) {
12035             parent = parent.parent();
12036             if (typeof(parent.labelAlign) !='undefined') {
12037                 return parent.labelAlign;
12038             }
12039         }
12040         return 'left';
12041         
12042     },
12043     
12044     getAutoCreate : function()
12045     {
12046         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12047         
12048         var id = Roo.id();
12049         
12050         var cfg = {};
12051         
12052         if(this.inputType != 'hidden'){
12053             cfg.cls = 'form-group' //input-group
12054         }
12055         
12056         var input =  {
12057             tag: 'input',
12058             id : id,
12059             type : this.inputType,
12060             value : this.value,
12061             cls : 'form-control',
12062             placeholder : this.placeholder || '',
12063             autocomplete : this.autocomplete || 'new-password'
12064         };
12065         if (this.inputType == 'file') {
12066             input.style = 'overflow:hidden'; // why not in CSS?
12067         }
12068         
12069         if(this.capture.length){
12070             input.capture = this.capture;
12071         }
12072         
12073         if(this.accept.length){
12074             input.accept = this.accept + "/*";
12075         }
12076         
12077         if(this.align){
12078             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12079         }
12080         
12081         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12082             input.maxLength = this.maxLength;
12083         }
12084         
12085         if (this.disabled) {
12086             input.disabled=true;
12087         }
12088         
12089         if (this.readOnly) {
12090             input.readonly=true;
12091         }
12092         
12093         if (this.name) {
12094             input.name = this.name;
12095         }
12096         
12097         if (this.size) {
12098             input.cls += ' input-' + this.size;
12099         }
12100         
12101         var settings=this;
12102         ['xs','sm','md','lg'].map(function(size){
12103             if (settings[size]) {
12104                 cfg.cls += ' col-' + size + '-' + settings[size];
12105             }
12106         });
12107         
12108         var inputblock = input;
12109         
12110         var feedback = {
12111             tag: 'span',
12112             cls: 'glyphicon form-control-feedback'
12113         };
12114             
12115         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12116             
12117             inputblock = {
12118                 cls : 'has-feedback',
12119                 cn :  [
12120                     input,
12121                     feedback
12122                 ] 
12123             };  
12124         }
12125         
12126         if (this.before || this.after) {
12127             
12128             inputblock = {
12129                 cls : 'input-group',
12130                 cn :  [] 
12131             };
12132             
12133             if (this.before && typeof(this.before) == 'string') {
12134                 
12135                 inputblock.cn.push({
12136                     tag :'span',
12137                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12138                     html : this.before
12139                 });
12140             }
12141             if (this.before && typeof(this.before) == 'object') {
12142                 this.before = Roo.factory(this.before);
12143                 
12144                 inputblock.cn.push({
12145                     tag :'span',
12146                     cls : 'roo-input-before input-group-prepend   input-group-' +
12147                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12148                 });
12149             }
12150             
12151             inputblock.cn.push(input);
12152             
12153             if (this.after && typeof(this.after) == 'string') {
12154                 inputblock.cn.push({
12155                     tag :'span',
12156                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12157                     html : this.after
12158                 });
12159             }
12160             if (this.after && typeof(this.after) == 'object') {
12161                 this.after = Roo.factory(this.after);
12162                 
12163                 inputblock.cn.push({
12164                     tag :'span',
12165                     cls : 'roo-input-after input-group-append  input-group-' +
12166                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12167                 });
12168             }
12169             
12170             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12171                 inputblock.cls += ' has-feedback';
12172                 inputblock.cn.push(feedback);
12173             }
12174         };
12175         var indicator = {
12176             tag : 'i',
12177             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12178             tooltip : 'This field is required'
12179         };
12180         if (this.allowBlank ) {
12181             indicator.style = this.allowBlank ? ' display:none' : '';
12182         }
12183         if (align ==='left' && this.fieldLabel.length) {
12184             
12185             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12186             
12187             cfg.cn = [
12188                 indicator,
12189                 {
12190                     tag: 'label',
12191                     'for' :  id,
12192                     cls : 'control-label col-form-label',
12193                     html : this.fieldLabel
12194
12195                 },
12196                 {
12197                     cls : "", 
12198                     cn: [
12199                         inputblock
12200                     ]
12201                 }
12202             ];
12203             
12204             var labelCfg = cfg.cn[1];
12205             var contentCfg = cfg.cn[2];
12206             
12207             if(this.indicatorpos == 'right'){
12208                 cfg.cn = [
12209                     {
12210                         tag: 'label',
12211                         'for' :  id,
12212                         cls : 'control-label col-form-label',
12213                         cn : [
12214                             {
12215                                 tag : 'span',
12216                                 html : this.fieldLabel
12217                             },
12218                             indicator
12219                         ]
12220                     },
12221                     {
12222                         cls : "",
12223                         cn: [
12224                             inputblock
12225                         ]
12226                     }
12227
12228                 ];
12229                 
12230                 labelCfg = cfg.cn[0];
12231                 contentCfg = cfg.cn[1];
12232             
12233             }
12234             
12235             if(this.labelWidth > 12){
12236                 labelCfg.style = "width: " + this.labelWidth + 'px';
12237             }
12238             
12239             if(this.labelWidth < 13 && this.labelmd == 0){
12240                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12241             }
12242             
12243             if(this.labellg > 0){
12244                 labelCfg.cls += ' col-lg-' + this.labellg;
12245                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12246             }
12247             
12248             if(this.labelmd > 0){
12249                 labelCfg.cls += ' col-md-' + this.labelmd;
12250                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12251             }
12252             
12253             if(this.labelsm > 0){
12254                 labelCfg.cls += ' col-sm-' + this.labelsm;
12255                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12256             }
12257             
12258             if(this.labelxs > 0){
12259                 labelCfg.cls += ' col-xs-' + this.labelxs;
12260                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12261             }
12262             
12263             
12264         } else if ( this.fieldLabel.length) {
12265                 
12266             
12267             
12268             cfg.cn = [
12269                 {
12270                     tag : 'i',
12271                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12272                     tooltip : 'This field is required',
12273                     style : this.allowBlank ? ' display:none' : '' 
12274                 },
12275                 {
12276                     tag: 'label',
12277                    //cls : 'input-group-addon',
12278                     html : this.fieldLabel
12279
12280                 },
12281
12282                inputblock
12283
12284            ];
12285            
12286            if(this.indicatorpos == 'right'){
12287        
12288                 cfg.cn = [
12289                     {
12290                         tag: 'label',
12291                        //cls : 'input-group-addon',
12292                         html : this.fieldLabel
12293
12294                     },
12295                     {
12296                         tag : 'i',
12297                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12298                         tooltip : 'This field is required',
12299                         style : this.allowBlank ? ' display:none' : '' 
12300                     },
12301
12302                    inputblock
12303
12304                ];
12305
12306             }
12307
12308         } else {
12309             
12310             cfg.cn = [
12311
12312                     inputblock
12313
12314             ];
12315                 
12316                 
12317         };
12318         
12319         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12320            cfg.cls += ' navbar-form';
12321         }
12322         
12323         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12324             // on BS4 we do this only if not form 
12325             cfg.cls += ' navbar-form';
12326             cfg.tag = 'li';
12327         }
12328         
12329         return cfg;
12330         
12331     },
12332     /**
12333      * return the real input element.
12334      */
12335     inputEl: function ()
12336     {
12337         return this.el.select('input.form-control',true).first();
12338     },
12339     
12340     tooltipEl : function()
12341     {
12342         return this.inputEl();
12343     },
12344     
12345     indicatorEl : function()
12346     {
12347         if (Roo.bootstrap.version == 4) {
12348             return false; // not enabled in v4 yet.
12349         }
12350         
12351         var indicator = this.el.select('i.roo-required-indicator',true).first();
12352         
12353         if(!indicator){
12354             return false;
12355         }
12356         
12357         return indicator;
12358         
12359     },
12360     
12361     setDisabled : function(v)
12362     {
12363         var i  = this.inputEl().dom;
12364         if (!v) {
12365             i.removeAttribute('disabled');
12366             return;
12367             
12368         }
12369         i.setAttribute('disabled','true');
12370     },
12371     initEvents : function()
12372     {
12373           
12374         this.inputEl().on("keydown" , this.fireKey,  this);
12375         this.inputEl().on("focus", this.onFocus,  this);
12376         this.inputEl().on("blur", this.onBlur,  this);
12377         
12378         this.inputEl().relayEvent('keyup', this);
12379         this.inputEl().relayEvent('paste', this);
12380         
12381         this.indicator = this.indicatorEl();
12382         
12383         if(this.indicator){
12384             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12385         }
12386  
12387         // reference to original value for reset
12388         this.originalValue = this.getValue();
12389         //Roo.form.TextField.superclass.initEvents.call(this);
12390         if(this.validationEvent == 'keyup'){
12391             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12392             this.inputEl().on('keyup', this.filterValidation, this);
12393         }
12394         else if(this.validationEvent !== false){
12395             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12396         }
12397         
12398         if(this.selectOnFocus){
12399             this.on("focus", this.preFocus, this);
12400             
12401         }
12402         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12403             this.inputEl().on("keypress", this.filterKeys, this);
12404         } else {
12405             this.inputEl().relayEvent('keypress', this);
12406         }
12407        /* if(this.grow){
12408             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12409             this.el.on("click", this.autoSize,  this);
12410         }
12411         */
12412         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12413             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12414         }
12415         
12416         if (typeof(this.before) == 'object') {
12417             this.before.render(this.el.select('.roo-input-before',true).first());
12418         }
12419         if (typeof(this.after) == 'object') {
12420             this.after.render(this.el.select('.roo-input-after',true).first());
12421         }
12422         
12423         this.inputEl().on('change', this.onChange, this);
12424         
12425     },
12426     filterValidation : function(e){
12427         if(!e.isNavKeyPress()){
12428             this.validationTask.delay(this.validationDelay);
12429         }
12430     },
12431      /**
12432      * Validates the field value
12433      * @return {Boolean} True if the value is valid, else false
12434      */
12435     validate : function(){
12436         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12437         if(this.disabled || this.validateValue(this.getRawValue())){
12438             this.markValid();
12439             return true;
12440         }
12441         
12442         this.markInvalid();
12443         return false;
12444     },
12445     
12446     
12447     /**
12448      * Validates a value according to the field's validation rules and marks the field as invalid
12449      * if the validation fails
12450      * @param {Mixed} value The value to validate
12451      * @return {Boolean} True if the value is valid, else false
12452      */
12453     validateValue : function(value)
12454     {
12455         if(this.getVisibilityEl().hasClass('hidden')){
12456             return true;
12457         }
12458         
12459         if(value.length < 1)  { // if it's blank
12460             if(this.allowBlank){
12461                 return true;
12462             }
12463             return false;
12464         }
12465         
12466         if(value.length < this.minLength){
12467             return false;
12468         }
12469         if(value.length > this.maxLength){
12470             return false;
12471         }
12472         if(this.vtype){
12473             var vt = Roo.form.VTypes;
12474             if(!vt[this.vtype](value, this)){
12475                 return false;
12476             }
12477         }
12478         if(typeof this.validator == "function"){
12479             var msg = this.validator(value);
12480             if(msg !== true){
12481                 return false;
12482             }
12483             if (typeof(msg) == 'string') {
12484                 this.invalidText = msg;
12485             }
12486         }
12487         
12488         if(this.regex && !this.regex.test(value)){
12489             return false;
12490         }
12491         
12492         return true;
12493     },
12494     
12495      // private
12496     fireKey : function(e){
12497         //Roo.log('field ' + e.getKey());
12498         if(e.isNavKeyPress()){
12499             this.fireEvent("specialkey", this, e);
12500         }
12501     },
12502     focus : function (selectText){
12503         if(this.rendered){
12504             this.inputEl().focus();
12505             if(selectText === true){
12506                 this.inputEl().dom.select();
12507             }
12508         }
12509         return this;
12510     } ,
12511     
12512     onFocus : function(){
12513         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12514            // this.el.addClass(this.focusClass);
12515         }
12516         if(!this.hasFocus){
12517             this.hasFocus = true;
12518             this.startValue = this.getValue();
12519             this.fireEvent("focus", this);
12520         }
12521     },
12522     
12523     beforeBlur : Roo.emptyFn,
12524
12525     
12526     // private
12527     onBlur : function(){
12528         this.beforeBlur();
12529         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12530             //this.el.removeClass(this.focusClass);
12531         }
12532         this.hasFocus = false;
12533         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12534             this.validate();
12535         }
12536         var v = this.getValue();
12537         if(String(v) !== String(this.startValue)){
12538             this.fireEvent('change', this, v, this.startValue);
12539         }
12540         this.fireEvent("blur", this);
12541     },
12542     
12543     onChange : function(e)
12544     {
12545         var v = this.getValue();
12546         if(String(v) !== String(this.startValue)){
12547             this.fireEvent('change', this, v, this.startValue);
12548         }
12549         
12550     },
12551     
12552     /**
12553      * Resets the current field value to the originally loaded value and clears any validation messages
12554      */
12555     reset : function(){
12556         this.setValue(this.originalValue);
12557         this.validate();
12558     },
12559      /**
12560      * Returns the name of the field
12561      * @return {Mixed} name The name field
12562      */
12563     getName: function(){
12564         return this.name;
12565     },
12566      /**
12567      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12568      * @return {Mixed} value The field value
12569      */
12570     getValue : function(){
12571         
12572         var v = this.inputEl().getValue();
12573         
12574         return v;
12575     },
12576     /**
12577      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12578      * @return {Mixed} value The field value
12579      */
12580     getRawValue : function(){
12581         var v = this.inputEl().getValue();
12582         
12583         return v;
12584     },
12585     
12586     /**
12587      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12588      * @param {Mixed} value The value to set
12589      */
12590     setRawValue : function(v){
12591         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12592     },
12593     
12594     selectText : function(start, end){
12595         var v = this.getRawValue();
12596         if(v.length > 0){
12597             start = start === undefined ? 0 : start;
12598             end = end === undefined ? v.length : end;
12599             var d = this.inputEl().dom;
12600             if(d.setSelectionRange){
12601                 d.setSelectionRange(start, end);
12602             }else if(d.createTextRange){
12603                 var range = d.createTextRange();
12604                 range.moveStart("character", start);
12605                 range.moveEnd("character", v.length-end);
12606                 range.select();
12607             }
12608         }
12609     },
12610     
12611     /**
12612      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12613      * @param {Mixed} value The value to set
12614      */
12615     setValue : function(v){
12616         this.value = v;
12617         if(this.rendered){
12618             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12619             this.validate();
12620         }
12621     },
12622     
12623     /*
12624     processValue : function(value){
12625         if(this.stripCharsRe){
12626             var newValue = value.replace(this.stripCharsRe, '');
12627             if(newValue !== value){
12628                 this.setRawValue(newValue);
12629                 return newValue;
12630             }
12631         }
12632         return value;
12633     },
12634   */
12635     preFocus : function(){
12636         
12637         if(this.selectOnFocus){
12638             this.inputEl().dom.select();
12639         }
12640     },
12641     filterKeys : function(e){
12642         var k = e.getKey();
12643         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12644             return;
12645         }
12646         var c = e.getCharCode(), cc = String.fromCharCode(c);
12647         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12648             return;
12649         }
12650         if(!this.maskRe.test(cc)){
12651             e.stopEvent();
12652         }
12653     },
12654      /**
12655      * Clear any invalid styles/messages for this field
12656      */
12657     clearInvalid : function(){
12658         
12659         if(!this.el || this.preventMark){ // not rendered
12660             return;
12661         }
12662         
12663         
12664         this.el.removeClass([this.invalidClass, 'is-invalid']);
12665         
12666         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12667             
12668             var feedback = this.el.select('.form-control-feedback', true).first();
12669             
12670             if(feedback){
12671                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12672             }
12673             
12674         }
12675         
12676         if(this.indicator){
12677             this.indicator.removeClass('visible');
12678             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12679         }
12680         
12681         this.fireEvent('valid', this);
12682     },
12683     
12684      /**
12685      * Mark this field as valid
12686      */
12687     markValid : function()
12688     {
12689         if(!this.el  || this.preventMark){ // not rendered...
12690             return;
12691         }
12692         
12693         this.el.removeClass([this.invalidClass, this.validClass]);
12694         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12695
12696         var feedback = this.el.select('.form-control-feedback', true).first();
12697             
12698         if(feedback){
12699             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12700         }
12701         
12702         if(this.indicator){
12703             this.indicator.removeClass('visible');
12704             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12705         }
12706         
12707         if(this.disabled){
12708             return;
12709         }
12710         
12711            
12712         if(this.allowBlank && !this.getRawValue().length){
12713             return;
12714         }
12715         if (Roo.bootstrap.version == 3) {
12716             this.el.addClass(this.validClass);
12717         } else {
12718             this.inputEl().addClass('is-valid');
12719         }
12720
12721         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12722             
12723             var feedback = this.el.select('.form-control-feedback', true).first();
12724             
12725             if(feedback){
12726                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12727                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12728             }
12729             
12730         }
12731         
12732         this.fireEvent('valid', this);
12733     },
12734     
12735      /**
12736      * Mark this field as invalid
12737      * @param {String} msg The validation message
12738      */
12739     markInvalid : function(msg)
12740     {
12741         if(!this.el  || this.preventMark){ // not rendered
12742             return;
12743         }
12744         
12745         this.el.removeClass([this.invalidClass, this.validClass]);
12746         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12747         
12748         var feedback = this.el.select('.form-control-feedback', true).first();
12749             
12750         if(feedback){
12751             this.el.select('.form-control-feedback', true).first().removeClass(
12752                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12753         }
12754
12755         if(this.disabled){
12756             return;
12757         }
12758         
12759         if(this.allowBlank && !this.getRawValue().length){
12760             return;
12761         }
12762         
12763         if(this.indicator){
12764             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12765             this.indicator.addClass('visible');
12766         }
12767         if (Roo.bootstrap.version == 3) {
12768             this.el.addClass(this.invalidClass);
12769         } else {
12770             this.inputEl().addClass('is-invalid');
12771         }
12772         
12773         
12774         
12775         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12776             
12777             var feedback = this.el.select('.form-control-feedback', true).first();
12778             
12779             if(feedback){
12780                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12781                 
12782                 if(this.getValue().length || this.forceFeedback){
12783                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12784                 }
12785                 
12786             }
12787             
12788         }
12789         
12790         this.fireEvent('invalid', this, msg);
12791     },
12792     // private
12793     SafariOnKeyDown : function(event)
12794     {
12795         // this is a workaround for a password hang bug on chrome/ webkit.
12796         if (this.inputEl().dom.type != 'password') {
12797             return;
12798         }
12799         
12800         var isSelectAll = false;
12801         
12802         if(this.inputEl().dom.selectionEnd > 0){
12803             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12804         }
12805         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12806             event.preventDefault();
12807             this.setValue('');
12808             return;
12809         }
12810         
12811         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12812             
12813             event.preventDefault();
12814             // this is very hacky as keydown always get's upper case.
12815             //
12816             var cc = String.fromCharCode(event.getCharCode());
12817             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12818             
12819         }
12820     },
12821     adjustWidth : function(tag, w){
12822         tag = tag.toLowerCase();
12823         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12824             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12825                 if(tag == 'input'){
12826                     return w + 2;
12827                 }
12828                 if(tag == 'textarea'){
12829                     return w-2;
12830                 }
12831             }else if(Roo.isOpera){
12832                 if(tag == 'input'){
12833                     return w + 2;
12834                 }
12835                 if(tag == 'textarea'){
12836                     return w-2;
12837                 }
12838             }
12839         }
12840         return w;
12841     },
12842     
12843     setFieldLabel : function(v)
12844     {
12845         if(!this.rendered){
12846             return;
12847         }
12848         
12849         if(this.indicatorEl()){
12850             var ar = this.el.select('label > span',true);
12851             
12852             if (ar.elements.length) {
12853                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12854                 this.fieldLabel = v;
12855                 return;
12856             }
12857             
12858             var br = this.el.select('label',true);
12859             
12860             if(br.elements.length) {
12861                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12862                 this.fieldLabel = v;
12863                 return;
12864             }
12865             
12866             Roo.log('Cannot Found any of label > span || label in input');
12867             return;
12868         }
12869         
12870         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12871         this.fieldLabel = v;
12872         
12873         
12874     }
12875 });
12876
12877  
12878 /*
12879  * - LGPL
12880  *
12881  * Input
12882  * 
12883  */
12884
12885 /**
12886  * @class Roo.bootstrap.TextArea
12887  * @extends Roo.bootstrap.Input
12888  * Bootstrap TextArea class
12889  * @cfg {Number} cols Specifies the visible width of a text area
12890  * @cfg {Number} rows Specifies the visible number of lines in a text area
12891  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12892  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12893  * @cfg {string} html text
12894  * 
12895  * @constructor
12896  * Create a new TextArea
12897  * @param {Object} config The config object
12898  */
12899
12900 Roo.bootstrap.TextArea = function(config){
12901     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12902    
12903 };
12904
12905 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12906      
12907     cols : false,
12908     rows : 5,
12909     readOnly : false,
12910     warp : 'soft',
12911     resize : false,
12912     value: false,
12913     html: false,
12914     
12915     getAutoCreate : function(){
12916         
12917         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12918         
12919         var id = Roo.id();
12920         
12921         var cfg = {};
12922         
12923         if(this.inputType != 'hidden'){
12924             cfg.cls = 'form-group' //input-group
12925         }
12926         
12927         var input =  {
12928             tag: 'textarea',
12929             id : id,
12930             warp : this.warp,
12931             rows : this.rows,
12932             value : this.value || '',
12933             html: this.html || '',
12934             cls : 'form-control',
12935             placeholder : this.placeholder || '' 
12936             
12937         };
12938         
12939         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12940             input.maxLength = this.maxLength;
12941         }
12942         
12943         if(this.resize){
12944             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12945         }
12946         
12947         if(this.cols){
12948             input.cols = this.cols;
12949         }
12950         
12951         if (this.readOnly) {
12952             input.readonly = true;
12953         }
12954         
12955         if (this.name) {
12956             input.name = this.name;
12957         }
12958         
12959         if (this.size) {
12960             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12961         }
12962         
12963         var settings=this;
12964         ['xs','sm','md','lg'].map(function(size){
12965             if (settings[size]) {
12966                 cfg.cls += ' col-' + size + '-' + settings[size];
12967             }
12968         });
12969         
12970         var inputblock = input;
12971         
12972         if(this.hasFeedback && !this.allowBlank){
12973             
12974             var feedback = {
12975                 tag: 'span',
12976                 cls: 'glyphicon form-control-feedback'
12977             };
12978
12979             inputblock = {
12980                 cls : 'has-feedback',
12981                 cn :  [
12982                     input,
12983                     feedback
12984                 ] 
12985             };  
12986         }
12987         
12988         
12989         if (this.before || this.after) {
12990             
12991             inputblock = {
12992                 cls : 'input-group',
12993                 cn :  [] 
12994             };
12995             if (this.before) {
12996                 inputblock.cn.push({
12997                     tag :'span',
12998                     cls : 'input-group-addon',
12999                     html : this.before
13000                 });
13001             }
13002             
13003             inputblock.cn.push(input);
13004             
13005             if(this.hasFeedback && !this.allowBlank){
13006                 inputblock.cls += ' has-feedback';
13007                 inputblock.cn.push(feedback);
13008             }
13009             
13010             if (this.after) {
13011                 inputblock.cn.push({
13012                     tag :'span',
13013                     cls : 'input-group-addon',
13014                     html : this.after
13015                 });
13016             }
13017             
13018         }
13019         
13020         if (align ==='left' && this.fieldLabel.length) {
13021             cfg.cn = [
13022                 {
13023                     tag: 'label',
13024                     'for' :  id,
13025                     cls : 'control-label',
13026                     html : this.fieldLabel
13027                 },
13028                 {
13029                     cls : "",
13030                     cn: [
13031                         inputblock
13032                     ]
13033                 }
13034
13035             ];
13036             
13037             if(this.labelWidth > 12){
13038                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13039             }
13040
13041             if(this.labelWidth < 13 && this.labelmd == 0){
13042                 this.labelmd = this.labelWidth;
13043             }
13044
13045             if(this.labellg > 0){
13046                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13047                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13048             }
13049
13050             if(this.labelmd > 0){
13051                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13052                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13053             }
13054
13055             if(this.labelsm > 0){
13056                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13057                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13058             }
13059
13060             if(this.labelxs > 0){
13061                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13062                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13063             }
13064             
13065         } else if ( this.fieldLabel.length) {
13066             cfg.cn = [
13067
13068                {
13069                    tag: 'label',
13070                    //cls : 'input-group-addon',
13071                    html : this.fieldLabel
13072
13073                },
13074
13075                inputblock
13076
13077            ];
13078
13079         } else {
13080
13081             cfg.cn = [
13082
13083                 inputblock
13084
13085             ];
13086                 
13087         }
13088         
13089         if (this.disabled) {
13090             input.disabled=true;
13091         }
13092         
13093         return cfg;
13094         
13095     },
13096     /**
13097      * return the real textarea element.
13098      */
13099     inputEl: function ()
13100     {
13101         return this.el.select('textarea.form-control',true).first();
13102     },
13103     
13104     /**
13105      * Clear any invalid styles/messages for this field
13106      */
13107     clearInvalid : function()
13108     {
13109         
13110         if(!this.el || this.preventMark){ // not rendered
13111             return;
13112         }
13113         
13114         var label = this.el.select('label', true).first();
13115         var icon = this.el.select('i.fa-star', true).first();
13116         
13117         if(label && icon){
13118             icon.remove();
13119         }
13120         this.el.removeClass( this.validClass);
13121         this.inputEl().removeClass('is-invalid');
13122          
13123         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13124             
13125             var feedback = this.el.select('.form-control-feedback', true).first();
13126             
13127             if(feedback){
13128                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13129             }
13130             
13131         }
13132         
13133         this.fireEvent('valid', this);
13134     },
13135     
13136      /**
13137      * Mark this field as valid
13138      */
13139     markValid : function()
13140     {
13141         if(!this.el  || this.preventMark){ // not rendered
13142             return;
13143         }
13144         
13145         this.el.removeClass([this.invalidClass, this.validClass]);
13146         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13147         
13148         var feedback = this.el.select('.form-control-feedback', true).first();
13149             
13150         if(feedback){
13151             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13152         }
13153
13154         if(this.disabled || this.allowBlank){
13155             return;
13156         }
13157         
13158         var label = this.el.select('label', true).first();
13159         var icon = this.el.select('i.fa-star', true).first();
13160         
13161         if(label && icon){
13162             icon.remove();
13163         }
13164         if (Roo.bootstrap.version == 3) {
13165             this.el.addClass(this.validClass);
13166         } else {
13167             this.inputEl().addClass('is-valid');
13168         }
13169         
13170         
13171         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13172             
13173             var feedback = this.el.select('.form-control-feedback', true).first();
13174             
13175             if(feedback){
13176                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13177                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13178             }
13179             
13180         }
13181         
13182         this.fireEvent('valid', this);
13183     },
13184     
13185      /**
13186      * Mark this field as invalid
13187      * @param {String} msg The validation message
13188      */
13189     markInvalid : function(msg)
13190     {
13191         if(!this.el  || this.preventMark){ // not rendered
13192             return;
13193         }
13194         
13195         this.el.removeClass([this.invalidClass, this.validClass]);
13196         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13197         
13198         var feedback = this.el.select('.form-control-feedback', true).first();
13199             
13200         if(feedback){
13201             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13202         }
13203
13204         if(this.disabled || this.allowBlank){
13205             return;
13206         }
13207         
13208         var label = this.el.select('label', true).first();
13209         var icon = this.el.select('i.fa-star', true).first();
13210         
13211         if(!this.getValue().length && label && !icon){
13212             this.el.createChild({
13213                 tag : 'i',
13214                 cls : 'text-danger fa fa-lg fa-star',
13215                 tooltip : 'This field is required',
13216                 style : 'margin-right:5px;'
13217             }, label, true);
13218         }
13219         
13220         if (Roo.bootstrap.version == 3) {
13221             this.el.addClass(this.invalidClass);
13222         } else {
13223             this.inputEl().addClass('is-invalid');
13224         }
13225         
13226         // fixme ... this may be depricated need to test..
13227         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13228             
13229             var feedback = this.el.select('.form-control-feedback', true).first();
13230             
13231             if(feedback){
13232                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13233                 
13234                 if(this.getValue().length || this.forceFeedback){
13235                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13236                 }
13237                 
13238             }
13239             
13240         }
13241         
13242         this.fireEvent('invalid', this, msg);
13243     }
13244 });
13245
13246  
13247 /*
13248  * - LGPL
13249  *
13250  * trigger field - base class for combo..
13251  * 
13252  */
13253  
13254 /**
13255  * @class Roo.bootstrap.TriggerField
13256  * @extends Roo.bootstrap.Input
13257  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13258  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13259  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13260  * for which you can provide a custom implementation.  For example:
13261  * <pre><code>
13262 var trigger = new Roo.bootstrap.TriggerField();
13263 trigger.onTriggerClick = myTriggerFn;
13264 trigger.applyTo('my-field');
13265 </code></pre>
13266  *
13267  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13268  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13269  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13270  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13271  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13272
13273  * @constructor
13274  * Create a new TriggerField.
13275  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13276  * to the base TextField)
13277  */
13278 Roo.bootstrap.TriggerField = function(config){
13279     this.mimicing = false;
13280     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13281 };
13282
13283 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13284     /**
13285      * @cfg {String} triggerClass A CSS class to apply to the trigger
13286      */
13287      /**
13288      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13289      */
13290     hideTrigger:false,
13291
13292     /**
13293      * @cfg {Boolean} removable (true|false) special filter default false
13294      */
13295     removable : false,
13296     
13297     /** @cfg {Boolean} grow @hide */
13298     /** @cfg {Number} growMin @hide */
13299     /** @cfg {Number} growMax @hide */
13300
13301     /**
13302      * @hide 
13303      * @method
13304      */
13305     autoSize: Roo.emptyFn,
13306     // private
13307     monitorTab : true,
13308     // private
13309     deferHeight : true,
13310
13311     
13312     actionMode : 'wrap',
13313     
13314     caret : false,
13315     
13316     
13317     getAutoCreate : function(){
13318        
13319         var align = this.labelAlign || this.parentLabelAlign();
13320         
13321         var id = Roo.id();
13322         
13323         var cfg = {
13324             cls: 'form-group' //input-group
13325         };
13326         
13327         
13328         var input =  {
13329             tag: 'input',
13330             id : id,
13331             type : this.inputType,
13332             cls : 'form-control',
13333             autocomplete: 'new-password',
13334             placeholder : this.placeholder || '' 
13335             
13336         };
13337         if (this.name) {
13338             input.name = this.name;
13339         }
13340         if (this.size) {
13341             input.cls += ' input-' + this.size;
13342         }
13343         
13344         if (this.disabled) {
13345             input.disabled=true;
13346         }
13347         
13348         var inputblock = input;
13349         
13350         if(this.hasFeedback && !this.allowBlank){
13351             
13352             var feedback = {
13353                 tag: 'span',
13354                 cls: 'glyphicon form-control-feedback'
13355             };
13356             
13357             if(this.removable && !this.editable  ){
13358                 inputblock = {
13359                     cls : 'has-feedback',
13360                     cn :  [
13361                         inputblock,
13362                         {
13363                             tag: 'button',
13364                             html : 'x',
13365                             cls : 'roo-combo-removable-btn close'
13366                         },
13367                         feedback
13368                     ] 
13369                 };
13370             } else {
13371                 inputblock = {
13372                     cls : 'has-feedback',
13373                     cn :  [
13374                         inputblock,
13375                         feedback
13376                     ] 
13377                 };
13378             }
13379
13380         } else {
13381             if(this.removable && !this.editable ){
13382                 inputblock = {
13383                     cls : 'roo-removable',
13384                     cn :  [
13385                         inputblock,
13386                         {
13387                             tag: 'button',
13388                             html : 'x',
13389                             cls : 'roo-combo-removable-btn close'
13390                         }
13391                     ] 
13392                 };
13393             }
13394         }
13395         
13396         if (this.before || this.after) {
13397             
13398             inputblock = {
13399                 cls : 'input-group',
13400                 cn :  [] 
13401             };
13402             if (this.before) {
13403                 inputblock.cn.push({
13404                     tag :'span',
13405                     cls : 'input-group-addon input-group-prepend input-group-text',
13406                     html : this.before
13407                 });
13408             }
13409             
13410             inputblock.cn.push(input);
13411             
13412             if(this.hasFeedback && !this.allowBlank){
13413                 inputblock.cls += ' has-feedback';
13414                 inputblock.cn.push(feedback);
13415             }
13416             
13417             if (this.after) {
13418                 inputblock.cn.push({
13419                     tag :'span',
13420                     cls : 'input-group-addon input-group-append input-group-text',
13421                     html : this.after
13422                 });
13423             }
13424             
13425         };
13426         
13427       
13428         
13429         var ibwrap = inputblock;
13430         
13431         if(this.multiple){
13432             ibwrap = {
13433                 tag: 'ul',
13434                 cls: 'roo-select2-choices',
13435                 cn:[
13436                     {
13437                         tag: 'li',
13438                         cls: 'roo-select2-search-field',
13439                         cn: [
13440
13441                             inputblock
13442                         ]
13443                     }
13444                 ]
13445             };
13446                 
13447         }
13448         
13449         var combobox = {
13450             cls: 'roo-select2-container input-group',
13451             cn: [
13452                  {
13453                     tag: 'input',
13454                     type : 'hidden',
13455                     cls: 'form-hidden-field'
13456                 },
13457                 ibwrap
13458             ]
13459         };
13460         
13461         if(!this.multiple && this.showToggleBtn){
13462             
13463             var caret = {
13464                         tag: 'span',
13465                         cls: 'caret'
13466              };
13467             if (this.caret != false) {
13468                 caret = {
13469                      tag: 'i',
13470                      cls: 'fa fa-' + this.caret
13471                 };
13472                 
13473             }
13474             
13475             combobox.cn.push({
13476                 tag :'span',
13477                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13478                 cn : [
13479                     Roo.bootstrap.version == 3 ? caret : '',
13480                     {
13481                         tag: 'span',
13482                         cls: 'combobox-clear',
13483                         cn  : [
13484                             {
13485                                 tag : 'i',
13486                                 cls: 'icon-remove'
13487                             }
13488                         ]
13489                     }
13490                 ]
13491
13492             })
13493         }
13494         
13495         if(this.multiple){
13496             combobox.cls += ' roo-select2-container-multi';
13497         }
13498          var indicator = {
13499             tag : 'i',
13500             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13501             tooltip : 'This field is required'
13502         };
13503         if (Roo.bootstrap.version == 4) {
13504             indicator = {
13505                 tag : 'i',
13506                 style : 'display:none'
13507             };
13508         }
13509         
13510         
13511         if (align ==='left' && this.fieldLabel.length) {
13512             
13513             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13514
13515             cfg.cn = [
13516                 indicator,
13517                 {
13518                     tag: 'label',
13519                     'for' :  id,
13520                     cls : 'control-label',
13521                     html : this.fieldLabel
13522
13523                 },
13524                 {
13525                     cls : "", 
13526                     cn: [
13527                         combobox
13528                     ]
13529                 }
13530
13531             ];
13532             
13533             var labelCfg = cfg.cn[1];
13534             var contentCfg = cfg.cn[2];
13535             
13536             if(this.indicatorpos == 'right'){
13537                 cfg.cn = [
13538                     {
13539                         tag: 'label',
13540                         'for' :  id,
13541                         cls : 'control-label',
13542                         cn : [
13543                             {
13544                                 tag : 'span',
13545                                 html : this.fieldLabel
13546                             },
13547                             indicator
13548                         ]
13549                     },
13550                     {
13551                         cls : "", 
13552                         cn: [
13553                             combobox
13554                         ]
13555                     }
13556
13557                 ];
13558                 
13559                 labelCfg = cfg.cn[0];
13560                 contentCfg = cfg.cn[1];
13561             }
13562             
13563             if(this.labelWidth > 12){
13564                 labelCfg.style = "width: " + this.labelWidth + 'px';
13565             }
13566             
13567             if(this.labelWidth < 13 && this.labelmd == 0){
13568                 this.labelmd = this.labelWidth;
13569             }
13570             
13571             if(this.labellg > 0){
13572                 labelCfg.cls += ' col-lg-' + this.labellg;
13573                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13574             }
13575             
13576             if(this.labelmd > 0){
13577                 labelCfg.cls += ' col-md-' + this.labelmd;
13578                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13579             }
13580             
13581             if(this.labelsm > 0){
13582                 labelCfg.cls += ' col-sm-' + this.labelsm;
13583                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13584             }
13585             
13586             if(this.labelxs > 0){
13587                 labelCfg.cls += ' col-xs-' + this.labelxs;
13588                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13589             }
13590             
13591         } else if ( this.fieldLabel.length) {
13592 //                Roo.log(" label");
13593             cfg.cn = [
13594                 indicator,
13595                {
13596                    tag: 'label',
13597                    //cls : 'input-group-addon',
13598                    html : this.fieldLabel
13599
13600                },
13601
13602                combobox
13603
13604             ];
13605             
13606             if(this.indicatorpos == 'right'){
13607                 
13608                 cfg.cn = [
13609                     {
13610                        tag: 'label',
13611                        cn : [
13612                            {
13613                                tag : 'span',
13614                                html : this.fieldLabel
13615                            },
13616                            indicator
13617                        ]
13618
13619                     },
13620                     combobox
13621
13622                 ];
13623
13624             }
13625
13626         } else {
13627             
13628 //                Roo.log(" no label && no align");
13629                 cfg = combobox
13630                      
13631                 
13632         }
13633         
13634         var settings=this;
13635         ['xs','sm','md','lg'].map(function(size){
13636             if (settings[size]) {
13637                 cfg.cls += ' col-' + size + '-' + settings[size];
13638             }
13639         });
13640         
13641         return cfg;
13642         
13643     },
13644     
13645     
13646     
13647     // private
13648     onResize : function(w, h){
13649 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13650 //        if(typeof w == 'number'){
13651 //            var x = w - this.trigger.getWidth();
13652 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13653 //            this.trigger.setStyle('left', x+'px');
13654 //        }
13655     },
13656
13657     // private
13658     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13659
13660     // private
13661     getResizeEl : function(){
13662         return this.inputEl();
13663     },
13664
13665     // private
13666     getPositionEl : function(){
13667         return this.inputEl();
13668     },
13669
13670     // private
13671     alignErrorIcon : function(){
13672         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13673     },
13674
13675     // private
13676     initEvents : function(){
13677         
13678         this.createList();
13679         
13680         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13681         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13682         if(!this.multiple && this.showToggleBtn){
13683             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13684             if(this.hideTrigger){
13685                 this.trigger.setDisplayed(false);
13686             }
13687             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13688         }
13689         
13690         if(this.multiple){
13691             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13692         }
13693         
13694         if(this.removable && !this.editable && !this.tickable){
13695             var close = this.closeTriggerEl();
13696             
13697             if(close){
13698                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13699                 close.on('click', this.removeBtnClick, this, close);
13700             }
13701         }
13702         
13703         //this.trigger.addClassOnOver('x-form-trigger-over');
13704         //this.trigger.addClassOnClick('x-form-trigger-click');
13705         
13706         //if(!this.width){
13707         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13708         //}
13709     },
13710     
13711     closeTriggerEl : function()
13712     {
13713         var close = this.el.select('.roo-combo-removable-btn', true).first();
13714         return close ? close : false;
13715     },
13716     
13717     removeBtnClick : function(e, h, el)
13718     {
13719         e.preventDefault();
13720         
13721         if(this.fireEvent("remove", this) !== false){
13722             this.reset();
13723             this.fireEvent("afterremove", this)
13724         }
13725     },
13726     
13727     createList : function()
13728     {
13729         this.list = Roo.get(document.body).createChild({
13730             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13731             cls: 'typeahead typeahead-long dropdown-menu shadow',
13732             style: 'display:none'
13733         });
13734         
13735         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13736         
13737     },
13738
13739     // private
13740     initTrigger : function(){
13741        
13742     },
13743
13744     // private
13745     onDestroy : function(){
13746         if(this.trigger){
13747             this.trigger.removeAllListeners();
13748           //  this.trigger.remove();
13749         }
13750         //if(this.wrap){
13751         //    this.wrap.remove();
13752         //}
13753         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13754     },
13755
13756     // private
13757     onFocus : function(){
13758         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13759         /*
13760         if(!this.mimicing){
13761             this.wrap.addClass('x-trigger-wrap-focus');
13762             this.mimicing = true;
13763             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13764             if(this.monitorTab){
13765                 this.el.on("keydown", this.checkTab, this);
13766             }
13767         }
13768         */
13769     },
13770
13771     // private
13772     checkTab : function(e){
13773         if(e.getKey() == e.TAB){
13774             this.triggerBlur();
13775         }
13776     },
13777
13778     // private
13779     onBlur : function(){
13780         // do nothing
13781     },
13782
13783     // private
13784     mimicBlur : function(e, t){
13785         /*
13786         if(!this.wrap.contains(t) && this.validateBlur()){
13787             this.triggerBlur();
13788         }
13789         */
13790     },
13791
13792     // private
13793     triggerBlur : function(){
13794         this.mimicing = false;
13795         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13796         if(this.monitorTab){
13797             this.el.un("keydown", this.checkTab, this);
13798         }
13799         //this.wrap.removeClass('x-trigger-wrap-focus');
13800         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13801     },
13802
13803     // private
13804     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13805     validateBlur : function(e, t){
13806         return true;
13807     },
13808
13809     // private
13810     onDisable : function(){
13811         this.inputEl().dom.disabled = true;
13812         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13813         //if(this.wrap){
13814         //    this.wrap.addClass('x-item-disabled');
13815         //}
13816     },
13817
13818     // private
13819     onEnable : function(){
13820         this.inputEl().dom.disabled = false;
13821         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13822         //if(this.wrap){
13823         //    this.el.removeClass('x-item-disabled');
13824         //}
13825     },
13826
13827     // private
13828     onShow : function(){
13829         var ae = this.getActionEl();
13830         
13831         if(ae){
13832             ae.dom.style.display = '';
13833             ae.dom.style.visibility = 'visible';
13834         }
13835     },
13836
13837     // private
13838     
13839     onHide : function(){
13840         var ae = this.getActionEl();
13841         ae.dom.style.display = 'none';
13842     },
13843
13844     /**
13845      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13846      * by an implementing function.
13847      * @method
13848      * @param {EventObject} e
13849      */
13850     onTriggerClick : Roo.emptyFn
13851 });
13852  
13853 /*
13854 * Licence: LGPL
13855 */
13856
13857 /**
13858  * @class Roo.bootstrap.CardUploader
13859  * @extends Roo.bootstrap.Button
13860  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13861  * @cfg {Number} errorTimeout default 3000
13862  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13863  * @cfg {Array}  html The button text.
13864
13865  *
13866  * @constructor
13867  * Create a new CardUploader
13868  * @param {Object} config The config object
13869  */
13870
13871 Roo.bootstrap.CardUploader = function(config){
13872     
13873  
13874     
13875     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13876     
13877     
13878     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13879         return r.data.id
13880      });
13881     
13882      this.addEvents({
13883          // raw events
13884         /**
13885          * @event preview
13886          * When a image is clicked on - and needs to display a slideshow or similar..
13887          * @param {Roo.bootstrap.Card} this
13888          * @param {Object} The image information data 
13889          *
13890          */
13891         'preview' : true,
13892          /**
13893          * @event download
13894          * When a the download link is clicked
13895          * @param {Roo.bootstrap.Card} this
13896          * @param {Object} The image information data  contains 
13897          */
13898         'download' : true
13899         
13900     });
13901 };
13902  
13903 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13904     
13905      
13906     errorTimeout : 3000,
13907      
13908     images : false,
13909    
13910     fileCollection : false,
13911     allowBlank : true,
13912     
13913     getAutoCreate : function()
13914     {
13915         
13916         var cfg =  {
13917             cls :'form-group' ,
13918             cn : [
13919                
13920                 {
13921                     tag: 'label',
13922                    //cls : 'input-group-addon',
13923                     html : this.fieldLabel
13924
13925                 },
13926
13927                 {
13928                     tag: 'input',
13929                     type : 'hidden',
13930                     name : this.name,
13931                     value : this.value,
13932                     cls : 'd-none  form-control'
13933                 },
13934                 
13935                 {
13936                     tag: 'input',
13937                     multiple : 'multiple',
13938                     type : 'file',
13939                     cls : 'd-none  roo-card-upload-selector'
13940                 },
13941                 
13942                 {
13943                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13944                 },
13945                 {
13946                     cls : 'card-columns roo-card-uploader-container'
13947                 }
13948
13949             ]
13950         };
13951            
13952          
13953         return cfg;
13954     },
13955     
13956     getChildContainer : function() /// what children are added to.
13957     {
13958         return this.containerEl;
13959     },
13960    
13961     getButtonContainer : function() /// what children are added to.
13962     {
13963         return this.el.select(".roo-card-uploader-button-container").first();
13964     },
13965    
13966     initEvents : function()
13967     {
13968         
13969         Roo.bootstrap.Input.prototype.initEvents.call(this);
13970         
13971         var t = this;
13972         this.addxtype({
13973             xns: Roo.bootstrap,
13974
13975             xtype : 'Button',
13976             container_method : 'getButtonContainer' ,            
13977             html :  this.html, // fix changable?
13978             cls : 'w-100 ',
13979             listeners : {
13980                 'click' : function(btn, e) {
13981                     t.onClick(e);
13982                 }
13983             }
13984         });
13985         
13986         
13987         
13988         
13989         this.urlAPI = (window.createObjectURL && window) || 
13990                                 (window.URL && URL.revokeObjectURL && URL) || 
13991                                 (window.webkitURL && webkitURL);
13992                         
13993          
13994          
13995          
13996         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13997         
13998         this.selectorEl.on('change', this.onFileSelected, this);
13999         if (this.images) {
14000             var t = this;
14001             this.images.forEach(function(img) {
14002                 t.addCard(img)
14003             });
14004             this.images = false;
14005         }
14006         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14007          
14008        
14009     },
14010     
14011    
14012     onClick : function(e)
14013     {
14014         e.preventDefault();
14015          
14016         this.selectorEl.dom.click();
14017          
14018     },
14019     
14020     onFileSelected : function(e)
14021     {
14022         e.preventDefault();
14023         
14024         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14025             return;
14026         }
14027         
14028         Roo.each(this.selectorEl.dom.files, function(file){    
14029             this.addFile(file);
14030         }, this);
14031          
14032     },
14033     
14034       
14035     
14036       
14037     
14038     addFile : function(file)
14039     {
14040            
14041         if(typeof(file) === 'string'){
14042             throw "Add file by name?"; // should not happen
14043             return;
14044         }
14045         
14046         if(!file || !this.urlAPI){
14047             return;
14048         }
14049         
14050         // file;
14051         // file.type;
14052         
14053         var _this = this;
14054         
14055         
14056         var url = _this.urlAPI.createObjectURL( file);
14057            
14058         this.addCard({
14059             id : Roo.bootstrap.CardUploader.ID--,
14060             is_uploaded : false,
14061             src : url,
14062             srcfile : file,
14063             title : file.name,
14064             mimetype : file.type,
14065             preview : false,
14066             is_deleted : 0
14067         });
14068         
14069     },
14070     
14071     /**
14072      * addCard - add an Attachment to the uploader
14073      * @param data - the data about the image to upload
14074      *
14075      * {
14076           id : 123
14077           title : "Title of file",
14078           is_uploaded : false,
14079           src : "http://.....",
14080           srcfile : { the File upload object },
14081           mimetype : file.type,
14082           preview : false,
14083           is_deleted : 0
14084           .. any other data...
14085         }
14086      *
14087      * 
14088     */
14089     
14090     addCard : function (data)
14091     {
14092         // hidden input element?
14093         // if the file is not an image...
14094         //then we need to use something other that and header_image
14095         var t = this;
14096         //   remove.....
14097         var footer = [
14098             {
14099                 xns : Roo.bootstrap,
14100                 xtype : 'CardFooter',
14101                  items: [
14102                     {
14103                         xns : Roo.bootstrap,
14104                         xtype : 'Element',
14105                         cls : 'd-flex',
14106                         items : [
14107                             
14108                             {
14109                                 xns : Roo.bootstrap,
14110                                 xtype : 'Button',
14111                                 html : String.format("<small>{0}</small>", data.title),
14112                                 cls : 'col-10 text-left',
14113                                 size: 'sm',
14114                                 weight: 'link',
14115                                 fa : 'download',
14116                                 listeners : {
14117                                     click : function() {
14118                                      
14119                                         t.fireEvent( "download", t, data );
14120                                     }
14121                                 }
14122                             },
14123                           
14124                             {
14125                                 xns : Roo.bootstrap,
14126                                 xtype : 'Button',
14127                                 style: 'max-height: 28px; ',
14128                                 size : 'sm',
14129                                 weight: 'danger',
14130                                 cls : 'col-2',
14131                                 fa : 'times',
14132                                 listeners : {
14133                                     click : function() {
14134                                         t.removeCard(data.id)
14135                                     }
14136                                 }
14137                             }
14138                         ]
14139                     }
14140                     
14141                 ] 
14142             }
14143             
14144         ];
14145         
14146         var cn = this.addxtype(
14147             {
14148                  
14149                 xns : Roo.bootstrap,
14150                 xtype : 'Card',
14151                 closeable : true,
14152                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14153                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14154                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14155                 data : data,
14156                 html : false,
14157                  
14158                 items : footer,
14159                 initEvents : function() {
14160                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14161                     var card = this;
14162                     this.imgEl = this.el.select('.card-img-top').first();
14163                     if (this.imgEl) {
14164                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14165                         this.imgEl.set({ 'pointer' : 'cursor' });
14166                                   
14167                     }
14168                     this.getCardFooter().addClass('p-1');
14169                     
14170                   
14171                 }
14172                 
14173             }
14174         );
14175         // dont' really need ot update items.
14176         // this.items.push(cn);
14177         this.fileCollection.add(cn);
14178         
14179         if (!data.srcfile) {
14180             this.updateInput();
14181             return;
14182         }
14183             
14184         var _t = this;
14185         var reader = new FileReader();
14186         reader.addEventListener("load", function() {  
14187             data.srcdata =  reader.result;
14188             _t.updateInput();
14189         });
14190         reader.readAsDataURL(data.srcfile);
14191         
14192         
14193         
14194     },
14195     removeCard : function(id)
14196     {
14197         
14198         var card  = this.fileCollection.get(id);
14199         card.data.is_deleted = 1;
14200         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14201         //this.fileCollection.remove(card);
14202         //this.items = this.items.filter(function(e) { return e != card });
14203         // dont' really need ot update items.
14204         card.el.dom.parentNode.removeChild(card.el.dom);
14205         this.updateInput();
14206
14207         
14208     },
14209     reset: function()
14210     {
14211         this.fileCollection.each(function(card) {
14212             if (card.el.dom && card.el.dom.parentNode) {
14213                 card.el.dom.parentNode.removeChild(card.el.dom);
14214             }
14215         });
14216         this.fileCollection.clear();
14217         this.updateInput();
14218     },
14219     
14220     updateInput : function()
14221     {
14222          var data = [];
14223         this.fileCollection.each(function(e) {
14224             data.push(e.data);
14225             
14226         });
14227         this.inputEl().dom.value = JSON.stringify(data);
14228         
14229         
14230         
14231     }
14232     
14233     
14234 });
14235
14236
14237 Roo.bootstrap.CardUploader.ID = -1;/*
14238  * Based on:
14239  * Ext JS Library 1.1.1
14240  * Copyright(c) 2006-2007, Ext JS, LLC.
14241  *
14242  * Originally Released Under LGPL - original licence link has changed is not relivant.
14243  *
14244  * Fork - LGPL
14245  * <script type="text/javascript">
14246  */
14247
14248
14249 /**
14250  * @class Roo.data.SortTypes
14251  * @singleton
14252  * Defines the default sorting (casting?) comparison functions used when sorting data.
14253  */
14254 Roo.data.SortTypes = {
14255     /**
14256      * Default sort that does nothing
14257      * @param {Mixed} s The value being converted
14258      * @return {Mixed} The comparison value
14259      */
14260     none : function(s){
14261         return s;
14262     },
14263     
14264     /**
14265      * The regular expression used to strip tags
14266      * @type {RegExp}
14267      * @property
14268      */
14269     stripTagsRE : /<\/?[^>]+>/gi,
14270     
14271     /**
14272      * Strips all HTML tags to sort on text only
14273      * @param {Mixed} s The value being converted
14274      * @return {String} The comparison value
14275      */
14276     asText : function(s){
14277         return String(s).replace(this.stripTagsRE, "");
14278     },
14279     
14280     /**
14281      * Strips all HTML tags to sort on text only - Case insensitive
14282      * @param {Mixed} s The value being converted
14283      * @return {String} The comparison value
14284      */
14285     asUCText : function(s){
14286         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14287     },
14288     
14289     /**
14290      * Case insensitive string
14291      * @param {Mixed} s The value being converted
14292      * @return {String} The comparison value
14293      */
14294     asUCString : function(s) {
14295         return String(s).toUpperCase();
14296     },
14297     
14298     /**
14299      * Date sorting
14300      * @param {Mixed} s The value being converted
14301      * @return {Number} The comparison value
14302      */
14303     asDate : function(s) {
14304         if(!s){
14305             return 0;
14306         }
14307         if(s instanceof Date){
14308             return s.getTime();
14309         }
14310         return Date.parse(String(s));
14311     },
14312     
14313     /**
14314      * Float sorting
14315      * @param {Mixed} s The value being converted
14316      * @return {Float} The comparison value
14317      */
14318     asFloat : function(s) {
14319         var val = parseFloat(String(s).replace(/,/g, ""));
14320         if(isNaN(val)) {
14321             val = 0;
14322         }
14323         return val;
14324     },
14325     
14326     /**
14327      * Integer sorting
14328      * @param {Mixed} s The value being converted
14329      * @return {Number} The comparison value
14330      */
14331     asInt : function(s) {
14332         var val = parseInt(String(s).replace(/,/g, ""));
14333         if(isNaN(val)) {
14334             val = 0;
14335         }
14336         return val;
14337     }
14338 };/*
14339  * Based on:
14340  * Ext JS Library 1.1.1
14341  * Copyright(c) 2006-2007, Ext JS, LLC.
14342  *
14343  * Originally Released Under LGPL - original licence link has changed is not relivant.
14344  *
14345  * Fork - LGPL
14346  * <script type="text/javascript">
14347  */
14348
14349 /**
14350 * @class Roo.data.Record
14351  * Instances of this class encapsulate both record <em>definition</em> information, and record
14352  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14353  * to access Records cached in an {@link Roo.data.Store} object.<br>
14354  * <p>
14355  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14356  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14357  * objects.<br>
14358  * <p>
14359  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14360  * @constructor
14361  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14362  * {@link #create}. The parameters are the same.
14363  * @param {Array} data An associative Array of data values keyed by the field name.
14364  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14365  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14366  * not specified an integer id is generated.
14367  */
14368 Roo.data.Record = function(data, id){
14369     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14370     this.data = data;
14371 };
14372
14373 /**
14374  * Generate a constructor for a specific record layout.
14375  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14376  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14377  * Each field definition object may contain the following properties: <ul>
14378  * <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,
14379  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14380  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14381  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14382  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14383  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14384  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14385  * this may be omitted.</p></li>
14386  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14387  * <ul><li>auto (Default, implies no conversion)</li>
14388  * <li>string</li>
14389  * <li>int</li>
14390  * <li>float</li>
14391  * <li>boolean</li>
14392  * <li>date</li></ul></p></li>
14393  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14394  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14395  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14396  * by the Reader into an object that will be stored in the Record. It is passed the
14397  * following parameters:<ul>
14398  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14399  * </ul></p></li>
14400  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14401  * </ul>
14402  * <br>usage:<br><pre><code>
14403 var TopicRecord = Roo.data.Record.create(
14404     {name: 'title', mapping: 'topic_title'},
14405     {name: 'author', mapping: 'username'},
14406     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14407     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14408     {name: 'lastPoster', mapping: 'user2'},
14409     {name: 'excerpt', mapping: 'post_text'}
14410 );
14411
14412 var myNewRecord = new TopicRecord({
14413     title: 'Do my job please',
14414     author: 'noobie',
14415     totalPosts: 1,
14416     lastPost: new Date(),
14417     lastPoster: 'Animal',
14418     excerpt: 'No way dude!'
14419 });
14420 myStore.add(myNewRecord);
14421 </code></pre>
14422  * @method create
14423  * @static
14424  */
14425 Roo.data.Record.create = function(o){
14426     var f = function(){
14427         f.superclass.constructor.apply(this, arguments);
14428     };
14429     Roo.extend(f, Roo.data.Record);
14430     var p = f.prototype;
14431     p.fields = new Roo.util.MixedCollection(false, function(field){
14432         return field.name;
14433     });
14434     for(var i = 0, len = o.length; i < len; i++){
14435         p.fields.add(new Roo.data.Field(o[i]));
14436     }
14437     f.getField = function(name){
14438         return p.fields.get(name);  
14439     };
14440     return f;
14441 };
14442
14443 Roo.data.Record.AUTO_ID = 1000;
14444 Roo.data.Record.EDIT = 'edit';
14445 Roo.data.Record.REJECT = 'reject';
14446 Roo.data.Record.COMMIT = 'commit';
14447
14448 Roo.data.Record.prototype = {
14449     /**
14450      * Readonly flag - true if this record has been modified.
14451      * @type Boolean
14452      */
14453     dirty : false,
14454     editing : false,
14455     error: null,
14456     modified: null,
14457
14458     // private
14459     join : function(store){
14460         this.store = store;
14461     },
14462
14463     /**
14464      * Set the named field to the specified value.
14465      * @param {String} name The name of the field to set.
14466      * @param {Object} value The value to set the field to.
14467      */
14468     set : function(name, value){
14469         if(this.data[name] == value){
14470             return;
14471         }
14472         this.dirty = true;
14473         if(!this.modified){
14474             this.modified = {};
14475         }
14476         if(typeof this.modified[name] == 'undefined'){
14477             this.modified[name] = this.data[name];
14478         }
14479         this.data[name] = value;
14480         if(!this.editing && this.store){
14481             this.store.afterEdit(this);
14482         }       
14483     },
14484
14485     /**
14486      * Get the value of the named field.
14487      * @param {String} name The name of the field to get the value of.
14488      * @return {Object} The value of the field.
14489      */
14490     get : function(name){
14491         return this.data[name]; 
14492     },
14493
14494     // private
14495     beginEdit : function(){
14496         this.editing = true;
14497         this.modified = {}; 
14498     },
14499
14500     // private
14501     cancelEdit : function(){
14502         this.editing = false;
14503         delete this.modified;
14504     },
14505
14506     // private
14507     endEdit : function(){
14508         this.editing = false;
14509         if(this.dirty && this.store){
14510             this.store.afterEdit(this);
14511         }
14512     },
14513
14514     /**
14515      * Usually called by the {@link Roo.data.Store} which owns the Record.
14516      * Rejects all changes made to the Record since either creation, or the last commit operation.
14517      * Modified fields are reverted to their original values.
14518      * <p>
14519      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14520      * of reject operations.
14521      */
14522     reject : function(){
14523         var m = this.modified;
14524         for(var n in m){
14525             if(typeof m[n] != "function"){
14526                 this.data[n] = m[n];
14527             }
14528         }
14529         this.dirty = false;
14530         delete this.modified;
14531         this.editing = false;
14532         if(this.store){
14533             this.store.afterReject(this);
14534         }
14535     },
14536
14537     /**
14538      * Usually called by the {@link Roo.data.Store} which owns the Record.
14539      * Commits all changes made to the Record since either creation, or the last commit operation.
14540      * <p>
14541      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14542      * of commit operations.
14543      */
14544     commit : function(){
14545         this.dirty = false;
14546         delete this.modified;
14547         this.editing = false;
14548         if(this.store){
14549             this.store.afterCommit(this);
14550         }
14551     },
14552
14553     // private
14554     hasError : function(){
14555         return this.error != null;
14556     },
14557
14558     // private
14559     clearError : function(){
14560         this.error = null;
14561     },
14562
14563     /**
14564      * Creates a copy of this record.
14565      * @param {String} id (optional) A new record id if you don't want to use this record's id
14566      * @return {Record}
14567      */
14568     copy : function(newId) {
14569         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14570     }
14571 };/*
14572  * Based on:
14573  * Ext JS Library 1.1.1
14574  * Copyright(c) 2006-2007, Ext JS, LLC.
14575  *
14576  * Originally Released Under LGPL - original licence link has changed is not relivant.
14577  *
14578  * Fork - LGPL
14579  * <script type="text/javascript">
14580  */
14581
14582
14583
14584 /**
14585  * @class Roo.data.Store
14586  * @extends Roo.util.Observable
14587  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14588  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14589  * <p>
14590  * 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
14591  * has no knowledge of the format of the data returned by the Proxy.<br>
14592  * <p>
14593  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14594  * instances from the data object. These records are cached and made available through accessor functions.
14595  * @constructor
14596  * Creates a new Store.
14597  * @param {Object} config A config object containing the objects needed for the Store to access data,
14598  * and read the data into Records.
14599  */
14600 Roo.data.Store = function(config){
14601     this.data = new Roo.util.MixedCollection(false);
14602     this.data.getKey = function(o){
14603         return o.id;
14604     };
14605     this.baseParams = {};
14606     // private
14607     this.paramNames = {
14608         "start" : "start",
14609         "limit" : "limit",
14610         "sort" : "sort",
14611         "dir" : "dir",
14612         "multisort" : "_multisort"
14613     };
14614
14615     if(config && config.data){
14616         this.inlineData = config.data;
14617         delete config.data;
14618     }
14619
14620     Roo.apply(this, config);
14621     
14622     if(this.reader){ // reader passed
14623         this.reader = Roo.factory(this.reader, Roo.data);
14624         this.reader.xmodule = this.xmodule || false;
14625         if(!this.recordType){
14626             this.recordType = this.reader.recordType;
14627         }
14628         if(this.reader.onMetaChange){
14629             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14630         }
14631     }
14632
14633     if(this.recordType){
14634         this.fields = this.recordType.prototype.fields;
14635     }
14636     this.modified = [];
14637
14638     this.addEvents({
14639         /**
14640          * @event datachanged
14641          * Fires when the data cache has changed, and a widget which is using this Store
14642          * as a Record cache should refresh its view.
14643          * @param {Store} this
14644          */
14645         datachanged : true,
14646         /**
14647          * @event metachange
14648          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14649          * @param {Store} this
14650          * @param {Object} meta The JSON metadata
14651          */
14652         metachange : true,
14653         /**
14654          * @event add
14655          * Fires when Records have been added to the Store
14656          * @param {Store} this
14657          * @param {Roo.data.Record[]} records The array of Records added
14658          * @param {Number} index The index at which the record(s) were added
14659          */
14660         add : true,
14661         /**
14662          * @event remove
14663          * Fires when a Record has been removed from the Store
14664          * @param {Store} this
14665          * @param {Roo.data.Record} record The Record that was removed
14666          * @param {Number} index The index at which the record was removed
14667          */
14668         remove : true,
14669         /**
14670          * @event update
14671          * Fires when a Record has been updated
14672          * @param {Store} this
14673          * @param {Roo.data.Record} record The Record that was updated
14674          * @param {String} operation The update operation being performed.  Value may be one of:
14675          * <pre><code>
14676  Roo.data.Record.EDIT
14677  Roo.data.Record.REJECT
14678  Roo.data.Record.COMMIT
14679          * </code></pre>
14680          */
14681         update : true,
14682         /**
14683          * @event clear
14684          * Fires when the data cache has been cleared.
14685          * @param {Store} this
14686          */
14687         clear : true,
14688         /**
14689          * @event beforeload
14690          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14691          * the load action will be canceled.
14692          * @param {Store} this
14693          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14694          */
14695         beforeload : true,
14696         /**
14697          * @event beforeloadadd
14698          * Fires after a new set of Records has been loaded.
14699          * @param {Store} this
14700          * @param {Roo.data.Record[]} records The Records that were loaded
14701          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14702          */
14703         beforeloadadd : true,
14704         /**
14705          * @event load
14706          * Fires after a new set of Records has been loaded, before they are added to the store.
14707          * @param {Store} this
14708          * @param {Roo.data.Record[]} records The Records that were loaded
14709          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14710          * @params {Object} return from reader
14711          */
14712         load : true,
14713         /**
14714          * @event loadexception
14715          * Fires if an exception occurs in the Proxy during loading.
14716          * Called with the signature of the Proxy's "loadexception" event.
14717          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14718          * 
14719          * @param {Proxy} 
14720          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14721          * @param {Object} load options 
14722          * @param {Object} jsonData from your request (normally this contains the Exception)
14723          */
14724         loadexception : true
14725     });
14726     
14727     if(this.proxy){
14728         this.proxy = Roo.factory(this.proxy, Roo.data);
14729         this.proxy.xmodule = this.xmodule || false;
14730         this.relayEvents(this.proxy,  ["loadexception"]);
14731     }
14732     this.sortToggle = {};
14733     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14734
14735     Roo.data.Store.superclass.constructor.call(this);
14736
14737     if(this.inlineData){
14738         this.loadData(this.inlineData);
14739         delete this.inlineData;
14740     }
14741 };
14742
14743 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14744      /**
14745     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14746     * without a remote query - used by combo/forms at present.
14747     */
14748     
14749     /**
14750     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14751     */
14752     /**
14753     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14754     */
14755     /**
14756     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14757     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14758     */
14759     /**
14760     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14761     * on any HTTP request
14762     */
14763     /**
14764     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14765     */
14766     /**
14767     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14768     */
14769     multiSort: false,
14770     /**
14771     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14772     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14773     */
14774     remoteSort : false,
14775
14776     /**
14777     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14778      * loaded or when a record is removed. (defaults to false).
14779     */
14780     pruneModifiedRecords : false,
14781
14782     // private
14783     lastOptions : null,
14784
14785     /**
14786      * Add Records to the Store and fires the add event.
14787      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14788      */
14789     add : function(records){
14790         records = [].concat(records);
14791         for(var i = 0, len = records.length; i < len; i++){
14792             records[i].join(this);
14793         }
14794         var index = this.data.length;
14795         this.data.addAll(records);
14796         this.fireEvent("add", this, records, index);
14797     },
14798
14799     /**
14800      * Remove a Record from the Store and fires the remove event.
14801      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14802      */
14803     remove : function(record){
14804         var index = this.data.indexOf(record);
14805         this.data.removeAt(index);
14806  
14807         if(this.pruneModifiedRecords){
14808             this.modified.remove(record);
14809         }
14810         this.fireEvent("remove", this, record, index);
14811     },
14812
14813     /**
14814      * Remove all Records from the Store and fires the clear event.
14815      */
14816     removeAll : function(){
14817         this.data.clear();
14818         if(this.pruneModifiedRecords){
14819             this.modified = [];
14820         }
14821         this.fireEvent("clear", this);
14822     },
14823
14824     /**
14825      * Inserts Records to the Store at the given index and fires the add event.
14826      * @param {Number} index The start index at which to insert the passed Records.
14827      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14828      */
14829     insert : function(index, records){
14830         records = [].concat(records);
14831         for(var i = 0, len = records.length; i < len; i++){
14832             this.data.insert(index, records[i]);
14833             records[i].join(this);
14834         }
14835         this.fireEvent("add", this, records, index);
14836     },
14837
14838     /**
14839      * Get the index within the cache of the passed Record.
14840      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14841      * @return {Number} The index of the passed Record. Returns -1 if not found.
14842      */
14843     indexOf : function(record){
14844         return this.data.indexOf(record);
14845     },
14846
14847     /**
14848      * Get the index within the cache of the Record with the passed id.
14849      * @param {String} id The id of the Record to find.
14850      * @return {Number} The index of the Record. Returns -1 if not found.
14851      */
14852     indexOfId : function(id){
14853         return this.data.indexOfKey(id);
14854     },
14855
14856     /**
14857      * Get the Record with the specified id.
14858      * @param {String} id The id of the Record to find.
14859      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14860      */
14861     getById : function(id){
14862         return this.data.key(id);
14863     },
14864
14865     /**
14866      * Get the Record at the specified index.
14867      * @param {Number} index The index of the Record to find.
14868      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14869      */
14870     getAt : function(index){
14871         return this.data.itemAt(index);
14872     },
14873
14874     /**
14875      * Returns a range of Records between specified indices.
14876      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14877      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14878      * @return {Roo.data.Record[]} An array of Records
14879      */
14880     getRange : function(start, end){
14881         return this.data.getRange(start, end);
14882     },
14883
14884     // private
14885     storeOptions : function(o){
14886         o = Roo.apply({}, o);
14887         delete o.callback;
14888         delete o.scope;
14889         this.lastOptions = o;
14890     },
14891
14892     /**
14893      * Loads the Record cache from the configured Proxy using the configured Reader.
14894      * <p>
14895      * If using remote paging, then the first load call must specify the <em>start</em>
14896      * and <em>limit</em> properties in the options.params property to establish the initial
14897      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14898      * <p>
14899      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14900      * and this call will return before the new data has been loaded. Perform any post-processing
14901      * in a callback function, or in a "load" event handler.</strong>
14902      * <p>
14903      * @param {Object} options An object containing properties which control loading options:<ul>
14904      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14905      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14906      * passed the following arguments:<ul>
14907      * <li>r : Roo.data.Record[]</li>
14908      * <li>options: Options object from the load call</li>
14909      * <li>success: Boolean success indicator</li></ul></li>
14910      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14911      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14912      * </ul>
14913      */
14914     load : function(options){
14915         options = options || {};
14916         if(this.fireEvent("beforeload", this, options) !== false){
14917             this.storeOptions(options);
14918             var p = Roo.apply(options.params || {}, this.baseParams);
14919             // if meta was not loaded from remote source.. try requesting it.
14920             if (!this.reader.metaFromRemote) {
14921                 p._requestMeta = 1;
14922             }
14923             if(this.sortInfo && this.remoteSort){
14924                 var pn = this.paramNames;
14925                 p[pn["sort"]] = this.sortInfo.field;
14926                 p[pn["dir"]] = this.sortInfo.direction;
14927             }
14928             if (this.multiSort) {
14929                 var pn = this.paramNames;
14930                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14931             }
14932             
14933             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14934         }
14935     },
14936
14937     /**
14938      * Reloads the Record cache from the configured Proxy using the configured Reader and
14939      * the options from the last load operation performed.
14940      * @param {Object} options (optional) An object containing properties which may override the options
14941      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14942      * the most recently used options are reused).
14943      */
14944     reload : function(options){
14945         this.load(Roo.applyIf(options||{}, this.lastOptions));
14946     },
14947
14948     // private
14949     // Called as a callback by the Reader during a load operation.
14950     loadRecords : function(o, options, success){
14951         if(!o || success === false){
14952             if(success !== false){
14953                 this.fireEvent("load", this, [], options, o);
14954             }
14955             if(options.callback){
14956                 options.callback.call(options.scope || this, [], options, false);
14957             }
14958             return;
14959         }
14960         // if data returned failure - throw an exception.
14961         if (o.success === false) {
14962             // show a message if no listener is registered.
14963             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14964                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14965             }
14966             // loadmask wil be hooked into this..
14967             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14968             return;
14969         }
14970         var r = o.records, t = o.totalRecords || r.length;
14971         
14972         this.fireEvent("beforeloadadd", this, r, options, o);
14973         
14974         if(!options || options.add !== true){
14975             if(this.pruneModifiedRecords){
14976                 this.modified = [];
14977             }
14978             for(var i = 0, len = r.length; i < len; i++){
14979                 r[i].join(this);
14980             }
14981             if(this.snapshot){
14982                 this.data = this.snapshot;
14983                 delete this.snapshot;
14984             }
14985             this.data.clear();
14986             this.data.addAll(r);
14987             this.totalLength = t;
14988             this.applySort();
14989             this.fireEvent("datachanged", this);
14990         }else{
14991             this.totalLength = Math.max(t, this.data.length+r.length);
14992             this.add(r);
14993         }
14994         
14995         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14996                 
14997             var e = new Roo.data.Record({});
14998
14999             e.set(this.parent.displayField, this.parent.emptyTitle);
15000             e.set(this.parent.valueField, '');
15001
15002             this.insert(0, e);
15003         }
15004             
15005         this.fireEvent("load", this, r, options, o);
15006         if(options.callback){
15007             options.callback.call(options.scope || this, r, options, true);
15008         }
15009     },
15010
15011
15012     /**
15013      * Loads data from a passed data block. A Reader which understands the format of the data
15014      * must have been configured in the constructor.
15015      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15016      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15017      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15018      */
15019     loadData : function(o, append){
15020         var r = this.reader.readRecords(o);
15021         this.loadRecords(r, {add: append}, true);
15022     },
15023     
15024      /**
15025      * using 'cn' the nested child reader read the child array into it's child stores.
15026      * @param {Object} rec The record with a 'children array
15027      */
15028     loadDataFromChildren : function(rec)
15029     {
15030         this.loadData(this.reader.toLoadData(rec));
15031     },
15032     
15033
15034     /**
15035      * Gets the number of cached records.
15036      * <p>
15037      * <em>If using paging, this may not be the total size of the dataset. If the data object
15038      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15039      * the data set size</em>
15040      */
15041     getCount : function(){
15042         return this.data.length || 0;
15043     },
15044
15045     /**
15046      * Gets the total number of records in the dataset as returned by the server.
15047      * <p>
15048      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15049      * the dataset size</em>
15050      */
15051     getTotalCount : function(){
15052         return this.totalLength || 0;
15053     },
15054
15055     /**
15056      * Returns the sort state of the Store as an object with two properties:
15057      * <pre><code>
15058  field {String} The name of the field by which the Records are sorted
15059  direction {String} The sort order, "ASC" or "DESC"
15060      * </code></pre>
15061      */
15062     getSortState : function(){
15063         return this.sortInfo;
15064     },
15065
15066     // private
15067     applySort : function(){
15068         if(this.sortInfo && !this.remoteSort){
15069             var s = this.sortInfo, f = s.field;
15070             var st = this.fields.get(f).sortType;
15071             var fn = function(r1, r2){
15072                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15073                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15074             };
15075             this.data.sort(s.direction, fn);
15076             if(this.snapshot && this.snapshot != this.data){
15077                 this.snapshot.sort(s.direction, fn);
15078             }
15079         }
15080     },
15081
15082     /**
15083      * Sets the default sort column and order to be used by the next load operation.
15084      * @param {String} fieldName The name of the field to sort by.
15085      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15086      */
15087     setDefaultSort : function(field, dir){
15088         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15089     },
15090
15091     /**
15092      * Sort the Records.
15093      * If remote sorting is used, the sort is performed on the server, and the cache is
15094      * reloaded. If local sorting is used, the cache is sorted internally.
15095      * @param {String} fieldName The name of the field to sort by.
15096      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15097      */
15098     sort : function(fieldName, dir){
15099         var f = this.fields.get(fieldName);
15100         if(!dir){
15101             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15102             
15103             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15104                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15105             }else{
15106                 dir = f.sortDir;
15107             }
15108         }
15109         this.sortToggle[f.name] = dir;
15110         this.sortInfo = {field: f.name, direction: dir};
15111         if(!this.remoteSort){
15112             this.applySort();
15113             this.fireEvent("datachanged", this);
15114         }else{
15115             this.load(this.lastOptions);
15116         }
15117     },
15118
15119     /**
15120      * Calls the specified function for each of the Records in the cache.
15121      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15122      * Returning <em>false</em> aborts and exits the iteration.
15123      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15124      */
15125     each : function(fn, scope){
15126         this.data.each(fn, scope);
15127     },
15128
15129     /**
15130      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15131      * (e.g., during paging).
15132      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15133      */
15134     getModifiedRecords : function(){
15135         return this.modified;
15136     },
15137
15138     // private
15139     createFilterFn : function(property, value, anyMatch){
15140         if(!value.exec){ // not a regex
15141             value = String(value);
15142             if(value.length == 0){
15143                 return false;
15144             }
15145             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15146         }
15147         return function(r){
15148             return value.test(r.data[property]);
15149         };
15150     },
15151
15152     /**
15153      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15154      * @param {String} property A field on your records
15155      * @param {Number} start The record index to start at (defaults to 0)
15156      * @param {Number} end The last record index to include (defaults to length - 1)
15157      * @return {Number} The sum
15158      */
15159     sum : function(property, start, end){
15160         var rs = this.data.items, v = 0;
15161         start = start || 0;
15162         end = (end || end === 0) ? end : rs.length-1;
15163
15164         for(var i = start; i <= end; i++){
15165             v += (rs[i].data[property] || 0);
15166         }
15167         return v;
15168     },
15169
15170     /**
15171      * Filter the records by a specified property.
15172      * @param {String} field A field on your records
15173      * @param {String/RegExp} value Either a string that the field
15174      * should start with or a RegExp to test against the field
15175      * @param {Boolean} anyMatch True to match any part not just the beginning
15176      */
15177     filter : function(property, value, anyMatch){
15178         var fn = this.createFilterFn(property, value, anyMatch);
15179         return fn ? this.filterBy(fn) : this.clearFilter();
15180     },
15181
15182     /**
15183      * Filter by a function. The specified function will be called with each
15184      * record in this data source. If the function returns true the record is included,
15185      * otherwise it is filtered.
15186      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15187      * @param {Object} scope (optional) The scope of the function (defaults to this)
15188      */
15189     filterBy : function(fn, scope){
15190         this.snapshot = this.snapshot || this.data;
15191         this.data = this.queryBy(fn, scope||this);
15192         this.fireEvent("datachanged", this);
15193     },
15194
15195     /**
15196      * Query the records by a specified property.
15197      * @param {String} field A field on your records
15198      * @param {String/RegExp} value Either a string that the field
15199      * should start with or a RegExp to test against the field
15200      * @param {Boolean} anyMatch True to match any part not just the beginning
15201      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15202      */
15203     query : function(property, value, anyMatch){
15204         var fn = this.createFilterFn(property, value, anyMatch);
15205         return fn ? this.queryBy(fn) : this.data.clone();
15206     },
15207
15208     /**
15209      * Query by a function. The specified function will be called with each
15210      * record in this data source. If the function returns true the record is included
15211      * in the results.
15212      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15213      * @param {Object} scope (optional) The scope of the function (defaults to this)
15214       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15215      **/
15216     queryBy : function(fn, scope){
15217         var data = this.snapshot || this.data;
15218         return data.filterBy(fn, scope||this);
15219     },
15220
15221     /**
15222      * Collects unique values for a particular dataIndex from this store.
15223      * @param {String} dataIndex The property to collect
15224      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15225      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15226      * @return {Array} An array of the unique values
15227      **/
15228     collect : function(dataIndex, allowNull, bypassFilter){
15229         var d = (bypassFilter === true && this.snapshot) ?
15230                 this.snapshot.items : this.data.items;
15231         var v, sv, r = [], l = {};
15232         for(var i = 0, len = d.length; i < len; i++){
15233             v = d[i].data[dataIndex];
15234             sv = String(v);
15235             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15236                 l[sv] = true;
15237                 r[r.length] = v;
15238             }
15239         }
15240         return r;
15241     },
15242
15243     /**
15244      * Revert to a view of the Record cache with no filtering applied.
15245      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15246      */
15247     clearFilter : function(suppressEvent){
15248         if(this.snapshot && this.snapshot != this.data){
15249             this.data = this.snapshot;
15250             delete this.snapshot;
15251             if(suppressEvent !== true){
15252                 this.fireEvent("datachanged", this);
15253             }
15254         }
15255     },
15256
15257     // private
15258     afterEdit : function(record){
15259         if(this.modified.indexOf(record) == -1){
15260             this.modified.push(record);
15261         }
15262         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15263     },
15264     
15265     // private
15266     afterReject : function(record){
15267         this.modified.remove(record);
15268         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15269     },
15270
15271     // private
15272     afterCommit : function(record){
15273         this.modified.remove(record);
15274         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15275     },
15276
15277     /**
15278      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15279      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15280      */
15281     commitChanges : function(){
15282         var m = this.modified.slice(0);
15283         this.modified = [];
15284         for(var i = 0, len = m.length; i < len; i++){
15285             m[i].commit();
15286         }
15287     },
15288
15289     /**
15290      * Cancel outstanding changes on all changed records.
15291      */
15292     rejectChanges : function(){
15293         var m = this.modified.slice(0);
15294         this.modified = [];
15295         for(var i = 0, len = m.length; i < len; i++){
15296             m[i].reject();
15297         }
15298     },
15299
15300     onMetaChange : function(meta, rtype, o){
15301         this.recordType = rtype;
15302         this.fields = rtype.prototype.fields;
15303         delete this.snapshot;
15304         this.sortInfo = meta.sortInfo || this.sortInfo;
15305         this.modified = [];
15306         this.fireEvent('metachange', this, this.reader.meta);
15307     },
15308     
15309     moveIndex : function(data, type)
15310     {
15311         var index = this.indexOf(data);
15312         
15313         var newIndex = index + type;
15314         
15315         this.remove(data);
15316         
15317         this.insert(newIndex, data);
15318         
15319     }
15320 });/*
15321  * Based on:
15322  * Ext JS Library 1.1.1
15323  * Copyright(c) 2006-2007, Ext JS, LLC.
15324  *
15325  * Originally Released Under LGPL - original licence link has changed is not relivant.
15326  *
15327  * Fork - LGPL
15328  * <script type="text/javascript">
15329  */
15330
15331 /**
15332  * @class Roo.data.SimpleStore
15333  * @extends Roo.data.Store
15334  * Small helper class to make creating Stores from Array data easier.
15335  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15336  * @cfg {Array} fields An array of field definition objects, or field name strings.
15337  * @cfg {Object} an existing reader (eg. copied from another store)
15338  * @cfg {Array} data The multi-dimensional array of data
15339  * @constructor
15340  * @param {Object} config
15341  */
15342 Roo.data.SimpleStore = function(config)
15343 {
15344     Roo.data.SimpleStore.superclass.constructor.call(this, {
15345         isLocal : true,
15346         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15347                 id: config.id
15348             },
15349             Roo.data.Record.create(config.fields)
15350         ),
15351         proxy : new Roo.data.MemoryProxy(config.data)
15352     });
15353     this.load();
15354 };
15355 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15356  * Based on:
15357  * Ext JS Library 1.1.1
15358  * Copyright(c) 2006-2007, Ext JS, LLC.
15359  *
15360  * Originally Released Under LGPL - original licence link has changed is not relivant.
15361  *
15362  * Fork - LGPL
15363  * <script type="text/javascript">
15364  */
15365
15366 /**
15367 /**
15368  * @extends Roo.data.Store
15369  * @class Roo.data.JsonStore
15370  * Small helper class to make creating Stores for JSON data easier. <br/>
15371 <pre><code>
15372 var store = new Roo.data.JsonStore({
15373     url: 'get-images.php',
15374     root: 'images',
15375     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15376 });
15377 </code></pre>
15378  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15379  * JsonReader and HttpProxy (unless inline data is provided).</b>
15380  * @cfg {Array} fields An array of field definition objects, or field name strings.
15381  * @constructor
15382  * @param {Object} config
15383  */
15384 Roo.data.JsonStore = function(c){
15385     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15386         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15387         reader: new Roo.data.JsonReader(c, c.fields)
15388     }));
15389 };
15390 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15391  * Based on:
15392  * Ext JS Library 1.1.1
15393  * Copyright(c) 2006-2007, Ext JS, LLC.
15394  *
15395  * Originally Released Under LGPL - original licence link has changed is not relivant.
15396  *
15397  * Fork - LGPL
15398  * <script type="text/javascript">
15399  */
15400
15401  
15402 Roo.data.Field = function(config){
15403     if(typeof config == "string"){
15404         config = {name: config};
15405     }
15406     Roo.apply(this, config);
15407     
15408     if(!this.type){
15409         this.type = "auto";
15410     }
15411     
15412     var st = Roo.data.SortTypes;
15413     // named sortTypes are supported, here we look them up
15414     if(typeof this.sortType == "string"){
15415         this.sortType = st[this.sortType];
15416     }
15417     
15418     // set default sortType for strings and dates
15419     if(!this.sortType){
15420         switch(this.type){
15421             case "string":
15422                 this.sortType = st.asUCString;
15423                 break;
15424             case "date":
15425                 this.sortType = st.asDate;
15426                 break;
15427             default:
15428                 this.sortType = st.none;
15429         }
15430     }
15431
15432     // define once
15433     var stripRe = /[\$,%]/g;
15434
15435     // prebuilt conversion function for this field, instead of
15436     // switching every time we're reading a value
15437     if(!this.convert){
15438         var cv, dateFormat = this.dateFormat;
15439         switch(this.type){
15440             case "":
15441             case "auto":
15442             case undefined:
15443                 cv = function(v){ return v; };
15444                 break;
15445             case "string":
15446                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15447                 break;
15448             case "int":
15449                 cv = function(v){
15450                     return v !== undefined && v !== null && v !== '' ?
15451                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15452                     };
15453                 break;
15454             case "float":
15455                 cv = function(v){
15456                     return v !== undefined && v !== null && v !== '' ?
15457                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15458                     };
15459                 break;
15460             case "bool":
15461             case "boolean":
15462                 cv = function(v){ return v === true || v === "true" || v == 1; };
15463                 break;
15464             case "date":
15465                 cv = function(v){
15466                     if(!v){
15467                         return '';
15468                     }
15469                     if(v instanceof Date){
15470                         return v;
15471                     }
15472                     if(dateFormat){
15473                         if(dateFormat == "timestamp"){
15474                             return new Date(v*1000);
15475                         }
15476                         return Date.parseDate(v, dateFormat);
15477                     }
15478                     var parsed = Date.parse(v);
15479                     return parsed ? new Date(parsed) : null;
15480                 };
15481              break;
15482             
15483         }
15484         this.convert = cv;
15485     }
15486 };
15487
15488 Roo.data.Field.prototype = {
15489     dateFormat: null,
15490     defaultValue: "",
15491     mapping: null,
15492     sortType : null,
15493     sortDir : "ASC"
15494 };/*
15495  * Based on:
15496  * Ext JS Library 1.1.1
15497  * Copyright(c) 2006-2007, Ext JS, LLC.
15498  *
15499  * Originally Released Under LGPL - original licence link has changed is not relivant.
15500  *
15501  * Fork - LGPL
15502  * <script type="text/javascript">
15503  */
15504  
15505 // Base class for reading structured data from a data source.  This class is intended to be
15506 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15507
15508 /**
15509  * @class Roo.data.DataReader
15510  * Base class for reading structured data from a data source.  This class is intended to be
15511  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15512  */
15513
15514 Roo.data.DataReader = function(meta, recordType){
15515     
15516     this.meta = meta;
15517     
15518     this.recordType = recordType instanceof Array ? 
15519         Roo.data.Record.create(recordType) : recordType;
15520 };
15521
15522 Roo.data.DataReader.prototype = {
15523     
15524     
15525     readerType : 'Data',
15526      /**
15527      * Create an empty record
15528      * @param {Object} data (optional) - overlay some values
15529      * @return {Roo.data.Record} record created.
15530      */
15531     newRow :  function(d) {
15532         var da =  {};
15533         this.recordType.prototype.fields.each(function(c) {
15534             switch( c.type) {
15535                 case 'int' : da[c.name] = 0; break;
15536                 case 'date' : da[c.name] = new Date(); break;
15537                 case 'float' : da[c.name] = 0.0; break;
15538                 case 'boolean' : da[c.name] = false; break;
15539                 default : da[c.name] = ""; break;
15540             }
15541             
15542         });
15543         return new this.recordType(Roo.apply(da, d));
15544     }
15545     
15546     
15547 };/*
15548  * Based on:
15549  * Ext JS Library 1.1.1
15550  * Copyright(c) 2006-2007, Ext JS, LLC.
15551  *
15552  * Originally Released Under LGPL - original licence link has changed is not relivant.
15553  *
15554  * Fork - LGPL
15555  * <script type="text/javascript">
15556  */
15557
15558 /**
15559  * @class Roo.data.DataProxy
15560  * @extends Roo.data.Observable
15561  * This class is an abstract base class for implementations which provide retrieval of
15562  * unformatted data objects.<br>
15563  * <p>
15564  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15565  * (of the appropriate type which knows how to parse the data object) to provide a block of
15566  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15567  * <p>
15568  * Custom implementations must implement the load method as described in
15569  * {@link Roo.data.HttpProxy#load}.
15570  */
15571 Roo.data.DataProxy = function(){
15572     this.addEvents({
15573         /**
15574          * @event beforeload
15575          * Fires before a network request is made to retrieve a data object.
15576          * @param {Object} This DataProxy object.
15577          * @param {Object} params The params parameter to the load function.
15578          */
15579         beforeload : true,
15580         /**
15581          * @event load
15582          * Fires before the load method's callback is called.
15583          * @param {Object} This DataProxy object.
15584          * @param {Object} o The data object.
15585          * @param {Object} arg The callback argument object passed to the load function.
15586          */
15587         load : true,
15588         /**
15589          * @event loadexception
15590          * Fires if an Exception occurs during data retrieval.
15591          * @param {Object} This DataProxy object.
15592          * @param {Object} o The data object.
15593          * @param {Object} arg The callback argument object passed to the load function.
15594          * @param {Object} e The Exception.
15595          */
15596         loadexception : true
15597     });
15598     Roo.data.DataProxy.superclass.constructor.call(this);
15599 };
15600
15601 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15602
15603     /**
15604      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15605      */
15606 /*
15607  * Based on:
15608  * Ext JS Library 1.1.1
15609  * Copyright(c) 2006-2007, Ext JS, LLC.
15610  *
15611  * Originally Released Under LGPL - original licence link has changed is not relivant.
15612  *
15613  * Fork - LGPL
15614  * <script type="text/javascript">
15615  */
15616 /**
15617  * @class Roo.data.MemoryProxy
15618  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15619  * to the Reader when its load method is called.
15620  * @constructor
15621  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15622  */
15623 Roo.data.MemoryProxy = function(data){
15624     if (data.data) {
15625         data = data.data;
15626     }
15627     Roo.data.MemoryProxy.superclass.constructor.call(this);
15628     this.data = data;
15629 };
15630
15631 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15632     
15633     /**
15634      * Load data from the requested source (in this case an in-memory
15635      * data object passed to the constructor), read the data object into
15636      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15637      * process that block using the passed callback.
15638      * @param {Object} params This parameter is not used by the MemoryProxy class.
15639      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15640      * object into a block of Roo.data.Records.
15641      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15642      * The function must be passed <ul>
15643      * <li>The Record block object</li>
15644      * <li>The "arg" argument from the load function</li>
15645      * <li>A boolean success indicator</li>
15646      * </ul>
15647      * @param {Object} scope The scope in which to call the callback
15648      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15649      */
15650     load : function(params, reader, callback, scope, arg){
15651         params = params || {};
15652         var result;
15653         try {
15654             result = reader.readRecords(params.data ? params.data :this.data);
15655         }catch(e){
15656             this.fireEvent("loadexception", this, arg, null, e);
15657             callback.call(scope, null, arg, false);
15658             return;
15659         }
15660         callback.call(scope, result, arg, true);
15661     },
15662     
15663     // private
15664     update : function(params, records){
15665         
15666     }
15667 });/*
15668  * Based on:
15669  * Ext JS Library 1.1.1
15670  * Copyright(c) 2006-2007, Ext JS, LLC.
15671  *
15672  * Originally Released Under LGPL - original licence link has changed is not relivant.
15673  *
15674  * Fork - LGPL
15675  * <script type="text/javascript">
15676  */
15677 /**
15678  * @class Roo.data.HttpProxy
15679  * @extends Roo.data.DataProxy
15680  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15681  * configured to reference a certain URL.<br><br>
15682  * <p>
15683  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15684  * from which the running page was served.<br><br>
15685  * <p>
15686  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15687  * <p>
15688  * Be aware that to enable the browser to parse an XML document, the server must set
15689  * the Content-Type header in the HTTP response to "text/xml".
15690  * @constructor
15691  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15692  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15693  * will be used to make the request.
15694  */
15695 Roo.data.HttpProxy = function(conn){
15696     Roo.data.HttpProxy.superclass.constructor.call(this);
15697     // is conn a conn config or a real conn?
15698     this.conn = conn;
15699     this.useAjax = !conn || !conn.events;
15700   
15701 };
15702
15703 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15704     // thse are take from connection...
15705     
15706     /**
15707      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15708      */
15709     /**
15710      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15711      * extra parameters to each request made by this object. (defaults to undefined)
15712      */
15713     /**
15714      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15715      *  to each request made by this object. (defaults to undefined)
15716      */
15717     /**
15718      * @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)
15719      */
15720     /**
15721      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15722      */
15723      /**
15724      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15725      * @type Boolean
15726      */
15727   
15728
15729     /**
15730      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15731      * @type Boolean
15732      */
15733     /**
15734      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15735      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15736      * a finer-grained basis than the DataProxy events.
15737      */
15738     getConnection : function(){
15739         return this.useAjax ? Roo.Ajax : this.conn;
15740     },
15741
15742     /**
15743      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15744      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15745      * process that block using the passed callback.
15746      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15747      * for the request to the remote server.
15748      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15749      * object into a block of Roo.data.Records.
15750      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15751      * The function must be passed <ul>
15752      * <li>The Record block object</li>
15753      * <li>The "arg" argument from the load function</li>
15754      * <li>A boolean success indicator</li>
15755      * </ul>
15756      * @param {Object} scope The scope in which to call the callback
15757      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15758      */
15759     load : function(params, reader, callback, scope, arg){
15760         if(this.fireEvent("beforeload", this, params) !== false){
15761             var  o = {
15762                 params : params || {},
15763                 request: {
15764                     callback : callback,
15765                     scope : scope,
15766                     arg : arg
15767                 },
15768                 reader: reader,
15769                 callback : this.loadResponse,
15770                 scope: this
15771             };
15772             if(this.useAjax){
15773                 Roo.applyIf(o, this.conn);
15774                 if(this.activeRequest){
15775                     Roo.Ajax.abort(this.activeRequest);
15776                 }
15777                 this.activeRequest = Roo.Ajax.request(o);
15778             }else{
15779                 this.conn.request(o);
15780             }
15781         }else{
15782             callback.call(scope||this, null, arg, false);
15783         }
15784     },
15785
15786     // private
15787     loadResponse : function(o, success, response){
15788         delete this.activeRequest;
15789         if(!success){
15790             this.fireEvent("loadexception", this, o, response);
15791             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15792             return;
15793         }
15794         var result;
15795         try {
15796             result = o.reader.read(response);
15797         }catch(e){
15798             this.fireEvent("loadexception", this, o, response, e);
15799             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15800             return;
15801         }
15802         
15803         this.fireEvent("load", this, o, o.request.arg);
15804         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15805     },
15806
15807     // private
15808     update : function(dataSet){
15809
15810     },
15811
15812     // private
15813     updateResponse : function(dataSet){
15814
15815     }
15816 });/*
15817  * Based on:
15818  * Ext JS Library 1.1.1
15819  * Copyright(c) 2006-2007, Ext JS, LLC.
15820  *
15821  * Originally Released Under LGPL - original licence link has changed is not relivant.
15822  *
15823  * Fork - LGPL
15824  * <script type="text/javascript">
15825  */
15826
15827 /**
15828  * @class Roo.data.ScriptTagProxy
15829  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15830  * other than the originating domain of the running page.<br><br>
15831  * <p>
15832  * <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
15833  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15834  * <p>
15835  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15836  * source code that is used as the source inside a &lt;script> tag.<br><br>
15837  * <p>
15838  * In order for the browser to process the returned data, the server must wrap the data object
15839  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15840  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15841  * depending on whether the callback name was passed:
15842  * <p>
15843  * <pre><code>
15844 boolean scriptTag = false;
15845 String cb = request.getParameter("callback");
15846 if (cb != null) {
15847     scriptTag = true;
15848     response.setContentType("text/javascript");
15849 } else {
15850     response.setContentType("application/x-json");
15851 }
15852 Writer out = response.getWriter();
15853 if (scriptTag) {
15854     out.write(cb + "(");
15855 }
15856 out.print(dataBlock.toJsonString());
15857 if (scriptTag) {
15858     out.write(");");
15859 }
15860 </pre></code>
15861  *
15862  * @constructor
15863  * @param {Object} config A configuration object.
15864  */
15865 Roo.data.ScriptTagProxy = function(config){
15866     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15867     Roo.apply(this, config);
15868     this.head = document.getElementsByTagName("head")[0];
15869 };
15870
15871 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15872
15873 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15874     /**
15875      * @cfg {String} url The URL from which to request the data object.
15876      */
15877     /**
15878      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15879      */
15880     timeout : 30000,
15881     /**
15882      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15883      * the server the name of the callback function set up by the load call to process the returned data object.
15884      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15885      * javascript output which calls this named function passing the data object as its only parameter.
15886      */
15887     callbackParam : "callback",
15888     /**
15889      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15890      * name to the request.
15891      */
15892     nocache : true,
15893
15894     /**
15895      * Load data from the configured URL, read the data object into
15896      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15897      * process that block using the passed callback.
15898      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15899      * for the request to the remote server.
15900      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15901      * object into a block of Roo.data.Records.
15902      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15903      * The function must be passed <ul>
15904      * <li>The Record block object</li>
15905      * <li>The "arg" argument from the load function</li>
15906      * <li>A boolean success indicator</li>
15907      * </ul>
15908      * @param {Object} scope The scope in which to call the callback
15909      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15910      */
15911     load : function(params, reader, callback, scope, arg){
15912         if(this.fireEvent("beforeload", this, params) !== false){
15913
15914             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15915
15916             var url = this.url;
15917             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15918             if(this.nocache){
15919                 url += "&_dc=" + (new Date().getTime());
15920             }
15921             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15922             var trans = {
15923                 id : transId,
15924                 cb : "stcCallback"+transId,
15925                 scriptId : "stcScript"+transId,
15926                 params : params,
15927                 arg : arg,
15928                 url : url,
15929                 callback : callback,
15930                 scope : scope,
15931                 reader : reader
15932             };
15933             var conn = this;
15934
15935             window[trans.cb] = function(o){
15936                 conn.handleResponse(o, trans);
15937             };
15938
15939             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15940
15941             if(this.autoAbort !== false){
15942                 this.abort();
15943             }
15944
15945             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15946
15947             var script = document.createElement("script");
15948             script.setAttribute("src", url);
15949             script.setAttribute("type", "text/javascript");
15950             script.setAttribute("id", trans.scriptId);
15951             this.head.appendChild(script);
15952
15953             this.trans = trans;
15954         }else{
15955             callback.call(scope||this, null, arg, false);
15956         }
15957     },
15958
15959     // private
15960     isLoading : function(){
15961         return this.trans ? true : false;
15962     },
15963
15964     /**
15965      * Abort the current server request.
15966      */
15967     abort : function(){
15968         if(this.isLoading()){
15969             this.destroyTrans(this.trans);
15970         }
15971     },
15972
15973     // private
15974     destroyTrans : function(trans, isLoaded){
15975         this.head.removeChild(document.getElementById(trans.scriptId));
15976         clearTimeout(trans.timeoutId);
15977         if(isLoaded){
15978             window[trans.cb] = undefined;
15979             try{
15980                 delete window[trans.cb];
15981             }catch(e){}
15982         }else{
15983             // if hasn't been loaded, wait for load to remove it to prevent script error
15984             window[trans.cb] = function(){
15985                 window[trans.cb] = undefined;
15986                 try{
15987                     delete window[trans.cb];
15988                 }catch(e){}
15989             };
15990         }
15991     },
15992
15993     // private
15994     handleResponse : function(o, trans){
15995         this.trans = false;
15996         this.destroyTrans(trans, true);
15997         var result;
15998         try {
15999             result = trans.reader.readRecords(o);
16000         }catch(e){
16001             this.fireEvent("loadexception", this, o, trans.arg, e);
16002             trans.callback.call(trans.scope||window, null, trans.arg, false);
16003             return;
16004         }
16005         this.fireEvent("load", this, o, trans.arg);
16006         trans.callback.call(trans.scope||window, result, trans.arg, true);
16007     },
16008
16009     // private
16010     handleFailure : function(trans){
16011         this.trans = false;
16012         this.destroyTrans(trans, false);
16013         this.fireEvent("loadexception", this, null, trans.arg);
16014         trans.callback.call(trans.scope||window, null, trans.arg, false);
16015     }
16016 });/*
16017  * Based on:
16018  * Ext JS Library 1.1.1
16019  * Copyright(c) 2006-2007, Ext JS, LLC.
16020  *
16021  * Originally Released Under LGPL - original licence link has changed is not relivant.
16022  *
16023  * Fork - LGPL
16024  * <script type="text/javascript">
16025  */
16026
16027 /**
16028  * @class Roo.data.JsonReader
16029  * @extends Roo.data.DataReader
16030  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16031  * based on mappings in a provided Roo.data.Record constructor.
16032  * 
16033  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16034  * in the reply previously. 
16035  * 
16036  * <p>
16037  * Example code:
16038  * <pre><code>
16039 var RecordDef = Roo.data.Record.create([
16040     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16041     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16042 ]);
16043 var myReader = new Roo.data.JsonReader({
16044     totalProperty: "results",    // The property which contains the total dataset size (optional)
16045     root: "rows",                // The property which contains an Array of row objects
16046     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16047 }, RecordDef);
16048 </code></pre>
16049  * <p>
16050  * This would consume a JSON file like this:
16051  * <pre><code>
16052 { 'results': 2, 'rows': [
16053     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16054     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16055 }
16056 </code></pre>
16057  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16058  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16059  * paged from the remote server.
16060  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16061  * @cfg {String} root name of the property which contains the Array of row objects.
16062  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16063  * @cfg {Array} fields Array of field definition objects
16064  * @constructor
16065  * Create a new JsonReader
16066  * @param {Object} meta Metadata configuration options
16067  * @param {Object} recordType Either an Array of field definition objects,
16068  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16069  */
16070 Roo.data.JsonReader = function(meta, recordType){
16071     
16072     meta = meta || {};
16073     // set some defaults:
16074     Roo.applyIf(meta, {
16075         totalProperty: 'total',
16076         successProperty : 'success',
16077         root : 'data',
16078         id : 'id'
16079     });
16080     
16081     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16082 };
16083 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16084     
16085     readerType : 'Json',
16086     
16087     /**
16088      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16089      * Used by Store query builder to append _requestMeta to params.
16090      * 
16091      */
16092     metaFromRemote : false,
16093     /**
16094      * This method is only used by a DataProxy which has retrieved data from a remote server.
16095      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16096      * @return {Object} data A data block which is used by an Roo.data.Store object as
16097      * a cache of Roo.data.Records.
16098      */
16099     read : function(response){
16100         var json = response.responseText;
16101        
16102         var o = /* eval:var:o */ eval("("+json+")");
16103         if(!o) {
16104             throw {message: "JsonReader.read: Json object not found"};
16105         }
16106         
16107         if(o.metaData){
16108             
16109             delete this.ef;
16110             this.metaFromRemote = true;
16111             this.meta = o.metaData;
16112             this.recordType = Roo.data.Record.create(o.metaData.fields);
16113             this.onMetaChange(this.meta, this.recordType, o);
16114         }
16115         return this.readRecords(o);
16116     },
16117
16118     // private function a store will implement
16119     onMetaChange : function(meta, recordType, o){
16120
16121     },
16122
16123     /**
16124          * @ignore
16125          */
16126     simpleAccess: function(obj, subsc) {
16127         return obj[subsc];
16128     },
16129
16130         /**
16131          * @ignore
16132          */
16133     getJsonAccessor: function(){
16134         var re = /[\[\.]/;
16135         return function(expr) {
16136             try {
16137                 return(re.test(expr))
16138                     ? new Function("obj", "return obj." + expr)
16139                     : function(obj){
16140                         return obj[expr];
16141                     };
16142             } catch(e){}
16143             return Roo.emptyFn;
16144         };
16145     }(),
16146
16147     /**
16148      * Create a data block containing Roo.data.Records from an XML document.
16149      * @param {Object} o An object which contains an Array of row objects in the property specified
16150      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16151      * which contains the total size of the dataset.
16152      * @return {Object} data A data block which is used by an Roo.data.Store object as
16153      * a cache of Roo.data.Records.
16154      */
16155     readRecords : function(o){
16156         /**
16157          * After any data loads, the raw JSON data is available for further custom processing.
16158          * @type Object
16159          */
16160         this.o = o;
16161         var s = this.meta, Record = this.recordType,
16162             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16163
16164 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16165         if (!this.ef) {
16166             if(s.totalProperty) {
16167                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16168                 }
16169                 if(s.successProperty) {
16170                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16171                 }
16172                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16173                 if (s.id) {
16174                         var g = this.getJsonAccessor(s.id);
16175                         this.getId = function(rec) {
16176                                 var r = g(rec);  
16177                                 return (r === undefined || r === "") ? null : r;
16178                         };
16179                 } else {
16180                         this.getId = function(){return null;};
16181                 }
16182             this.ef = [];
16183             for(var jj = 0; jj < fl; jj++){
16184                 f = fi[jj];
16185                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16186                 this.ef[jj] = this.getJsonAccessor(map);
16187             }
16188         }
16189
16190         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16191         if(s.totalProperty){
16192             var vt = parseInt(this.getTotal(o), 10);
16193             if(!isNaN(vt)){
16194                 totalRecords = vt;
16195             }
16196         }
16197         if(s.successProperty){
16198             var vs = this.getSuccess(o);
16199             if(vs === false || vs === 'false'){
16200                 success = false;
16201             }
16202         }
16203         var records = [];
16204         for(var i = 0; i < c; i++){
16205                 var n = root[i];
16206             var values = {};
16207             var id = this.getId(n);
16208             for(var j = 0; j < fl; j++){
16209                 f = fi[j];
16210             var v = this.ef[j](n);
16211             if (!f.convert) {
16212                 Roo.log('missing convert for ' + f.name);
16213                 Roo.log(f);
16214                 continue;
16215             }
16216             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16217             }
16218             var record = new Record(values, id);
16219             record.json = n;
16220             records[i] = record;
16221         }
16222         return {
16223             raw : o,
16224             success : success,
16225             records : records,
16226             totalRecords : totalRecords
16227         };
16228     },
16229     // used when loading children.. @see loadDataFromChildren
16230     toLoadData: function(rec)
16231     {
16232         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16233         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16234         return { data : data, total : data.length };
16235         
16236     }
16237 });/*
16238  * Based on:
16239  * Ext JS Library 1.1.1
16240  * Copyright(c) 2006-2007, Ext JS, LLC.
16241  *
16242  * Originally Released Under LGPL - original licence link has changed is not relivant.
16243  *
16244  * Fork - LGPL
16245  * <script type="text/javascript">
16246  */
16247
16248 /**
16249  * @class Roo.data.ArrayReader
16250  * @extends Roo.data.DataReader
16251  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16252  * Each element of that Array represents a row of data fields. The
16253  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16254  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16255  * <p>
16256  * Example code:.
16257  * <pre><code>
16258 var RecordDef = Roo.data.Record.create([
16259     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16260     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16261 ]);
16262 var myReader = new Roo.data.ArrayReader({
16263     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16264 }, RecordDef);
16265 </code></pre>
16266  * <p>
16267  * This would consume an Array like this:
16268  * <pre><code>
16269 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16270   </code></pre>
16271  
16272  * @constructor
16273  * Create a new JsonReader
16274  * @param {Object} meta Metadata configuration options.
16275  * @param {Object|Array} recordType Either an Array of field definition objects
16276  * 
16277  * @cfg {Array} fields Array of field definition objects
16278  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16279  * as specified to {@link Roo.data.Record#create},
16280  * or an {@link Roo.data.Record} object
16281  *
16282  * 
16283  * created using {@link Roo.data.Record#create}.
16284  */
16285 Roo.data.ArrayReader = function(meta, recordType)
16286 {    
16287     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16288 };
16289
16290 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16291     
16292       /**
16293      * Create a data block containing Roo.data.Records from an XML document.
16294      * @param {Object} o An Array of row objects which represents the dataset.
16295      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16296      * a cache of Roo.data.Records.
16297      */
16298     readRecords : function(o)
16299     {
16300         var sid = this.meta ? this.meta.id : null;
16301         var recordType = this.recordType, fields = recordType.prototype.fields;
16302         var records = [];
16303         var root = o;
16304         for(var i = 0; i < root.length; i++){
16305             var n = root[i];
16306             var values = {};
16307             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16308             for(var j = 0, jlen = fields.length; j < jlen; j++){
16309                 var f = fields.items[j];
16310                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16311                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16312                 v = f.convert(v);
16313                 values[f.name] = v;
16314             }
16315             var record = new recordType(values, id);
16316             record.json = n;
16317             records[records.length] = record;
16318         }
16319         return {
16320             records : records,
16321             totalRecords : records.length
16322         };
16323     },
16324     // used when loading children.. @see loadDataFromChildren
16325     toLoadData: function(rec)
16326     {
16327         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16328         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16329         
16330     }
16331     
16332     
16333 });/*
16334  * - LGPL
16335  * * 
16336  */
16337
16338 /**
16339  * @class Roo.bootstrap.ComboBox
16340  * @extends Roo.bootstrap.TriggerField
16341  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16342  * @cfg {Boolean} append (true|false) default false
16343  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16344  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16345  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16346  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16347  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16348  * @cfg {Boolean} animate default true
16349  * @cfg {Boolean} emptyResultText only for touch device
16350  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16351  * @cfg {String} emptyTitle default ''
16352  * @cfg {Number} width fixed with? experimental
16353  * @constructor
16354  * Create a new ComboBox.
16355  * @param {Object} config Configuration options
16356  */
16357 Roo.bootstrap.ComboBox = function(config){
16358     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16359     this.addEvents({
16360         /**
16361          * @event expand
16362          * Fires when the dropdown list is expanded
16363         * @param {Roo.bootstrap.ComboBox} combo This combo box
16364         */
16365         'expand' : true,
16366         /**
16367          * @event collapse
16368          * Fires when the dropdown list is collapsed
16369         * @param {Roo.bootstrap.ComboBox} combo This combo box
16370         */
16371         'collapse' : true,
16372         /**
16373          * @event beforeselect
16374          * Fires before a list item is selected. Return false to cancel the selection.
16375         * @param {Roo.bootstrap.ComboBox} combo This combo box
16376         * @param {Roo.data.Record} record The data record returned from the underlying store
16377         * @param {Number} index The index of the selected item in the dropdown list
16378         */
16379         'beforeselect' : true,
16380         /**
16381          * @event select
16382          * Fires when a list item is selected
16383         * @param {Roo.bootstrap.ComboBox} combo This combo box
16384         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16385         * @param {Number} index The index of the selected item in the dropdown list
16386         */
16387         'select' : true,
16388         /**
16389          * @event beforequery
16390          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16391          * The event object passed has these properties:
16392         * @param {Roo.bootstrap.ComboBox} combo This combo box
16393         * @param {String} query The query
16394         * @param {Boolean} forceAll true to force "all" query
16395         * @param {Boolean} cancel true to cancel the query
16396         * @param {Object} e The query event object
16397         */
16398         'beforequery': true,
16399          /**
16400          * @event add
16401          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16402         * @param {Roo.bootstrap.ComboBox} combo This combo box
16403         */
16404         'add' : true,
16405         /**
16406          * @event edit
16407          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16408         * @param {Roo.bootstrap.ComboBox} combo This combo box
16409         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16410         */
16411         'edit' : true,
16412         /**
16413          * @event remove
16414          * Fires when the remove value from the combobox array
16415         * @param {Roo.bootstrap.ComboBox} combo This combo box
16416         */
16417         'remove' : true,
16418         /**
16419          * @event afterremove
16420          * Fires when the remove value from the combobox array
16421         * @param {Roo.bootstrap.ComboBox} combo This combo box
16422         */
16423         'afterremove' : true,
16424         /**
16425          * @event specialfilter
16426          * Fires when specialfilter
16427             * @param {Roo.bootstrap.ComboBox} combo This combo box
16428             */
16429         'specialfilter' : true,
16430         /**
16431          * @event tick
16432          * Fires when tick the element
16433             * @param {Roo.bootstrap.ComboBox} combo This combo box
16434             */
16435         'tick' : true,
16436         /**
16437          * @event touchviewdisplay
16438          * Fires when touch view require special display (default is using displayField)
16439             * @param {Roo.bootstrap.ComboBox} combo This combo box
16440             * @param {Object} cfg set html .
16441             */
16442         'touchviewdisplay' : true
16443         
16444     });
16445     
16446     this.item = [];
16447     this.tickItems = [];
16448     
16449     this.selectedIndex = -1;
16450     if(this.mode == 'local'){
16451         if(config.queryDelay === undefined){
16452             this.queryDelay = 10;
16453         }
16454         if(config.minChars === undefined){
16455             this.minChars = 0;
16456         }
16457     }
16458 };
16459
16460 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16461      
16462     /**
16463      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16464      * rendering into an Roo.Editor, defaults to false)
16465      */
16466     /**
16467      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16468      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16469      */
16470     /**
16471      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16472      */
16473     /**
16474      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16475      * the dropdown list (defaults to undefined, with no header element)
16476      */
16477
16478      /**
16479      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16480      */
16481      
16482      /**
16483      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16484      */
16485     listWidth: undefined,
16486     /**
16487      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16488      * mode = 'remote' or 'text' if mode = 'local')
16489      */
16490     displayField: undefined,
16491     
16492     /**
16493      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16494      * mode = 'remote' or 'value' if mode = 'local'). 
16495      * Note: use of a valueField requires the user make a selection
16496      * in order for a value to be mapped.
16497      */
16498     valueField: undefined,
16499     /**
16500      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16501      */
16502     modalTitle : '',
16503     
16504     /**
16505      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16506      * field's data value (defaults to the underlying DOM element's name)
16507      */
16508     hiddenName: undefined,
16509     /**
16510      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16511      */
16512     listClass: '',
16513     /**
16514      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16515      */
16516     selectedClass: 'active',
16517     
16518     /**
16519      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16520      */
16521     shadow:'sides',
16522     /**
16523      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16524      * anchor positions (defaults to 'tl-bl')
16525      */
16526     listAlign: 'tl-bl?',
16527     /**
16528      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16529      */
16530     maxHeight: 300,
16531     /**
16532      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16533      * query specified by the allQuery config option (defaults to 'query')
16534      */
16535     triggerAction: 'query',
16536     /**
16537      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16538      * (defaults to 4, does not apply if editable = false)
16539      */
16540     minChars : 4,
16541     /**
16542      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16543      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16544      */
16545     typeAhead: false,
16546     /**
16547      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16548      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16549      */
16550     queryDelay: 500,
16551     /**
16552      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16553      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16554      */
16555     pageSize: 0,
16556     /**
16557      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16558      * when editable = true (defaults to false)
16559      */
16560     selectOnFocus:false,
16561     /**
16562      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16563      */
16564     queryParam: 'query',
16565     /**
16566      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16567      * when mode = 'remote' (defaults to 'Loading...')
16568      */
16569     loadingText: 'Loading...',
16570     /**
16571      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16572      */
16573     resizable: false,
16574     /**
16575      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16576      */
16577     handleHeight : 8,
16578     /**
16579      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16580      * traditional select (defaults to true)
16581      */
16582     editable: true,
16583     /**
16584      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16585      */
16586     allQuery: '',
16587     /**
16588      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16589      */
16590     mode: 'remote',
16591     /**
16592      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16593      * listWidth has a higher value)
16594      */
16595     minListWidth : 70,
16596     /**
16597      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16598      * allow the user to set arbitrary text into the field (defaults to false)
16599      */
16600     forceSelection:false,
16601     /**
16602      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16603      * if typeAhead = true (defaults to 250)
16604      */
16605     typeAheadDelay : 250,
16606     /**
16607      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16608      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16609      */
16610     valueNotFoundText : undefined,
16611     /**
16612      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16613      */
16614     blockFocus : false,
16615     
16616     /**
16617      * @cfg {Boolean} disableClear Disable showing of clear button.
16618      */
16619     disableClear : false,
16620     /**
16621      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16622      */
16623     alwaysQuery : false,
16624     
16625     /**
16626      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16627      */
16628     multiple : false,
16629     
16630     /**
16631      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16632      */
16633     invalidClass : "has-warning",
16634     
16635     /**
16636      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16637      */
16638     validClass : "has-success",
16639     
16640     /**
16641      * @cfg {Boolean} specialFilter (true|false) special filter default false
16642      */
16643     specialFilter : false,
16644     
16645     /**
16646      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16647      */
16648     mobileTouchView : true,
16649     
16650     /**
16651      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16652      */
16653     useNativeIOS : false,
16654     
16655     /**
16656      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16657      */
16658     mobile_restrict_height : false,
16659     
16660     ios_options : false,
16661     
16662     //private
16663     addicon : false,
16664     editicon: false,
16665     
16666     page: 0,
16667     hasQuery: false,
16668     append: false,
16669     loadNext: false,
16670     autoFocus : true,
16671     tickable : false,
16672     btnPosition : 'right',
16673     triggerList : true,
16674     showToggleBtn : true,
16675     animate : true,
16676     emptyResultText: 'Empty',
16677     triggerText : 'Select',
16678     emptyTitle : '',
16679     width : false,
16680     
16681     // element that contains real text value.. (when hidden is used..)
16682     
16683     getAutoCreate : function()
16684     {   
16685         var cfg = false;
16686         //render
16687         /*
16688          * Render classic select for iso
16689          */
16690         
16691         if(Roo.isIOS && this.useNativeIOS){
16692             cfg = this.getAutoCreateNativeIOS();
16693             return cfg;
16694         }
16695         
16696         /*
16697          * Touch Devices
16698          */
16699         
16700         if(Roo.isTouch && this.mobileTouchView){
16701             cfg = this.getAutoCreateTouchView();
16702             return cfg;;
16703         }
16704         
16705         /*
16706          *  Normal ComboBox
16707          */
16708         if(!this.tickable){
16709             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16710             return cfg;
16711         }
16712         
16713         /*
16714          *  ComboBox with tickable selections
16715          */
16716              
16717         var align = this.labelAlign || this.parentLabelAlign();
16718         
16719         cfg = {
16720             cls : 'form-group roo-combobox-tickable' //input-group
16721         };
16722         
16723         var btn_text_select = '';
16724         var btn_text_done = '';
16725         var btn_text_cancel = '';
16726         
16727         if (this.btn_text_show) {
16728             btn_text_select = 'Select';
16729             btn_text_done = 'Done';
16730             btn_text_cancel = 'Cancel'; 
16731         }
16732         
16733         var buttons = {
16734             tag : 'div',
16735             cls : 'tickable-buttons',
16736             cn : [
16737                 {
16738                     tag : 'button',
16739                     type : 'button',
16740                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16741                     //html : this.triggerText
16742                     html: btn_text_select
16743                 },
16744                 {
16745                     tag : 'button',
16746                     type : 'button',
16747                     name : 'ok',
16748                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16749                     //html : 'Done'
16750                     html: btn_text_done
16751                 },
16752                 {
16753                     tag : 'button',
16754                     type : 'button',
16755                     name : 'cancel',
16756                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16757                     //html : 'Cancel'
16758                     html: btn_text_cancel
16759                 }
16760             ]
16761         };
16762         
16763         if(this.editable){
16764             buttons.cn.unshift({
16765                 tag: 'input',
16766                 cls: 'roo-select2-search-field-input'
16767             });
16768         }
16769         
16770         var _this = this;
16771         
16772         Roo.each(buttons.cn, function(c){
16773             if (_this.size) {
16774                 c.cls += ' btn-' + _this.size;
16775             }
16776
16777             if (_this.disabled) {
16778                 c.disabled = true;
16779             }
16780         });
16781         
16782         var box = {
16783             tag: 'div',
16784             style : 'display: contents',
16785             cn: [
16786                 {
16787                     tag: 'input',
16788                     type : 'hidden',
16789                     cls: 'form-hidden-field'
16790                 },
16791                 {
16792                     tag: 'ul',
16793                     cls: 'roo-select2-choices',
16794                     cn:[
16795                         {
16796                             tag: 'li',
16797                             cls: 'roo-select2-search-field',
16798                             cn: [
16799                                 buttons
16800                             ]
16801                         }
16802                     ]
16803                 }
16804             ]
16805         };
16806         
16807         var combobox = {
16808             cls: 'roo-select2-container input-group roo-select2-container-multi',
16809             cn: [
16810                 
16811                 box
16812 //                {
16813 //                    tag: 'ul',
16814 //                    cls: 'typeahead typeahead-long dropdown-menu',
16815 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16816 //                }
16817             ]
16818         };
16819         
16820         if(this.hasFeedback && !this.allowBlank){
16821             
16822             var feedback = {
16823                 tag: 'span',
16824                 cls: 'glyphicon form-control-feedback'
16825             };
16826
16827             combobox.cn.push(feedback);
16828         }
16829         
16830         
16831         
16832         var indicator = {
16833             tag : 'i',
16834             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16835             tooltip : 'This field is required'
16836         };
16837         if (Roo.bootstrap.version == 4) {
16838             indicator = {
16839                 tag : 'i',
16840                 style : 'display:none'
16841             };
16842         }
16843         if (align ==='left' && this.fieldLabel.length) {
16844             
16845             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16846             
16847             cfg.cn = [
16848                 indicator,
16849                 {
16850                     tag: 'label',
16851                     'for' :  id,
16852                     cls : 'control-label col-form-label',
16853                     html : this.fieldLabel
16854
16855                 },
16856                 {
16857                     cls : "", 
16858                     cn: [
16859                         combobox
16860                     ]
16861                 }
16862
16863             ];
16864             
16865             var labelCfg = cfg.cn[1];
16866             var contentCfg = cfg.cn[2];
16867             
16868
16869             if(this.indicatorpos == 'right'){
16870                 
16871                 cfg.cn = [
16872                     {
16873                         tag: 'label',
16874                         'for' :  id,
16875                         cls : 'control-label col-form-label',
16876                         cn : [
16877                             {
16878                                 tag : 'span',
16879                                 html : this.fieldLabel
16880                             },
16881                             indicator
16882                         ]
16883                     },
16884                     {
16885                         cls : "",
16886                         cn: [
16887                             combobox
16888                         ]
16889                     }
16890
16891                 ];
16892                 
16893                 
16894                 
16895                 labelCfg = cfg.cn[0];
16896                 contentCfg = cfg.cn[1];
16897             
16898             }
16899             
16900             if(this.labelWidth > 12){
16901                 labelCfg.style = "width: " + this.labelWidth + 'px';
16902             }
16903             if(this.width * 1 > 0){
16904                 contentCfg.style = "width: " + this.width + 'px';
16905             }
16906             if(this.labelWidth < 13 && this.labelmd == 0){
16907                 this.labelmd = this.labelWidth;
16908             }
16909             
16910             if(this.labellg > 0){
16911                 labelCfg.cls += ' col-lg-' + this.labellg;
16912                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16913             }
16914             
16915             if(this.labelmd > 0){
16916                 labelCfg.cls += ' col-md-' + this.labelmd;
16917                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16918             }
16919             
16920             if(this.labelsm > 0){
16921                 labelCfg.cls += ' col-sm-' + this.labelsm;
16922                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16923             }
16924             
16925             if(this.labelxs > 0){
16926                 labelCfg.cls += ' col-xs-' + this.labelxs;
16927                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16928             }
16929                 
16930                 
16931         } else if ( this.fieldLabel.length) {
16932 //                Roo.log(" label");
16933                  cfg.cn = [
16934                    indicator,
16935                     {
16936                         tag: 'label',
16937                         //cls : 'input-group-addon',
16938                         html : this.fieldLabel
16939                     },
16940                     combobox
16941                 ];
16942                 
16943                 if(this.indicatorpos == 'right'){
16944                     cfg.cn = [
16945                         {
16946                             tag: 'label',
16947                             //cls : 'input-group-addon',
16948                             html : this.fieldLabel
16949                         },
16950                         indicator,
16951                         combobox
16952                     ];
16953                     
16954                 }
16955
16956         } else {
16957             
16958 //                Roo.log(" no label && no align");
16959                 cfg = combobox
16960                      
16961                 
16962         }
16963          
16964         var settings=this;
16965         ['xs','sm','md','lg'].map(function(size){
16966             if (settings[size]) {
16967                 cfg.cls += ' col-' + size + '-' + settings[size];
16968             }
16969         });
16970         
16971         return cfg;
16972         
16973     },
16974     
16975     _initEventsCalled : false,
16976     
16977     // private
16978     initEvents: function()
16979     {   
16980         if (this._initEventsCalled) { // as we call render... prevent looping...
16981             return;
16982         }
16983         this._initEventsCalled = true;
16984         
16985         if (!this.store) {
16986             throw "can not find store for combo";
16987         }
16988         
16989         this.indicator = this.indicatorEl();
16990         
16991         this.store = Roo.factory(this.store, Roo.data);
16992         this.store.parent = this;
16993         
16994         // if we are building from html. then this element is so complex, that we can not really
16995         // use the rendered HTML.
16996         // so we have to trash and replace the previous code.
16997         if (Roo.XComponent.build_from_html) {
16998             // remove this element....
16999             var e = this.el.dom, k=0;
17000             while (e ) { e = e.previousSibling;  ++k;}
17001
17002             this.el.remove();
17003             
17004             this.el=false;
17005             this.rendered = false;
17006             
17007             this.render(this.parent().getChildContainer(true), k);
17008         }
17009         
17010         if(Roo.isIOS && this.useNativeIOS){
17011             this.initIOSView();
17012             return;
17013         }
17014         
17015         /*
17016          * Touch Devices
17017          */
17018         
17019         if(Roo.isTouch && this.mobileTouchView){
17020             this.initTouchView();
17021             return;
17022         }
17023         
17024         if(this.tickable){
17025             this.initTickableEvents();
17026             return;
17027         }
17028         
17029         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17030         
17031         if(this.hiddenName){
17032             
17033             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17034             
17035             this.hiddenField.dom.value =
17036                 this.hiddenValue !== undefined ? this.hiddenValue :
17037                 this.value !== undefined ? this.value : '';
17038
17039             // prevent input submission
17040             this.el.dom.removeAttribute('name');
17041             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17042              
17043              
17044         }
17045         //if(Roo.isGecko){
17046         //    this.el.dom.setAttribute('autocomplete', 'off');
17047         //}
17048         
17049         var cls = 'x-combo-list';
17050         
17051         //this.list = new Roo.Layer({
17052         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17053         //});
17054         
17055         var _this = this;
17056         
17057         (function(){
17058             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17059             _this.list.setWidth(lw);
17060         }).defer(100);
17061         
17062         this.list.on('mouseover', this.onViewOver, this);
17063         this.list.on('mousemove', this.onViewMove, this);
17064         this.list.on('scroll', this.onViewScroll, this);
17065         
17066         /*
17067         this.list.swallowEvent('mousewheel');
17068         this.assetHeight = 0;
17069
17070         if(this.title){
17071             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17072             this.assetHeight += this.header.getHeight();
17073         }
17074
17075         this.innerList = this.list.createChild({cls:cls+'-inner'});
17076         this.innerList.on('mouseover', this.onViewOver, this);
17077         this.innerList.on('mousemove', this.onViewMove, this);
17078         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17079         
17080         if(this.allowBlank && !this.pageSize && !this.disableClear){
17081             this.footer = this.list.createChild({cls:cls+'-ft'});
17082             this.pageTb = new Roo.Toolbar(this.footer);
17083            
17084         }
17085         if(this.pageSize){
17086             this.footer = this.list.createChild({cls:cls+'-ft'});
17087             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17088                     {pageSize: this.pageSize});
17089             
17090         }
17091         
17092         if (this.pageTb && this.allowBlank && !this.disableClear) {
17093             var _this = this;
17094             this.pageTb.add(new Roo.Toolbar.Fill(), {
17095                 cls: 'x-btn-icon x-btn-clear',
17096                 text: '&#160;',
17097                 handler: function()
17098                 {
17099                     _this.collapse();
17100                     _this.clearValue();
17101                     _this.onSelect(false, -1);
17102                 }
17103             });
17104         }
17105         if (this.footer) {
17106             this.assetHeight += this.footer.getHeight();
17107         }
17108         */
17109             
17110         if(!this.tpl){
17111             this.tpl = Roo.bootstrap.version == 4 ?
17112                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17113                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17114         }
17115
17116         this.view = new Roo.View(this.list, this.tpl, {
17117             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17118         });
17119         //this.view.wrapEl.setDisplayed(false);
17120         this.view.on('click', this.onViewClick, this);
17121         
17122         
17123         this.store.on('beforeload', this.onBeforeLoad, this);
17124         this.store.on('load', this.onLoad, this);
17125         this.store.on('loadexception', this.onLoadException, this);
17126         /*
17127         if(this.resizable){
17128             this.resizer = new Roo.Resizable(this.list,  {
17129                pinned:true, handles:'se'
17130             });
17131             this.resizer.on('resize', function(r, w, h){
17132                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17133                 this.listWidth = w;
17134                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17135                 this.restrictHeight();
17136             }, this);
17137             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17138         }
17139         */
17140         if(!this.editable){
17141             this.editable = true;
17142             this.setEditable(false);
17143         }
17144         
17145         /*
17146         
17147         if (typeof(this.events.add.listeners) != 'undefined') {
17148             
17149             this.addicon = this.wrap.createChild(
17150                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17151        
17152             this.addicon.on('click', function(e) {
17153                 this.fireEvent('add', this);
17154             }, this);
17155         }
17156         if (typeof(this.events.edit.listeners) != 'undefined') {
17157             
17158             this.editicon = this.wrap.createChild(
17159                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17160             if (this.addicon) {
17161                 this.editicon.setStyle('margin-left', '40px');
17162             }
17163             this.editicon.on('click', function(e) {
17164                 
17165                 // we fire even  if inothing is selected..
17166                 this.fireEvent('edit', this, this.lastData );
17167                 
17168             }, this);
17169         }
17170         */
17171         
17172         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17173             "up" : function(e){
17174                 this.inKeyMode = true;
17175                 this.selectPrev();
17176             },
17177
17178             "down" : function(e){
17179                 if(!this.isExpanded()){
17180                     this.onTriggerClick();
17181                 }else{
17182                     this.inKeyMode = true;
17183                     this.selectNext();
17184                 }
17185             },
17186
17187             "enter" : function(e){
17188 //                this.onViewClick();
17189                 //return true;
17190                 this.collapse();
17191                 
17192                 if(this.fireEvent("specialkey", this, e)){
17193                     this.onViewClick(false);
17194                 }
17195                 
17196                 return true;
17197             },
17198
17199             "esc" : function(e){
17200                 this.collapse();
17201             },
17202
17203             "tab" : function(e){
17204                 this.collapse();
17205                 
17206                 if(this.fireEvent("specialkey", this, e)){
17207                     this.onViewClick(false);
17208                 }
17209                 
17210                 return true;
17211             },
17212
17213             scope : this,
17214
17215             doRelay : function(foo, bar, hname){
17216                 if(hname == 'down' || this.scope.isExpanded()){
17217                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17218                 }
17219                 return true;
17220             },
17221
17222             forceKeyDown: true
17223         });
17224         
17225         
17226         this.queryDelay = Math.max(this.queryDelay || 10,
17227                 this.mode == 'local' ? 10 : 250);
17228         
17229         
17230         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17231         
17232         if(this.typeAhead){
17233             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17234         }
17235         if(this.editable !== false){
17236             this.inputEl().on("keyup", this.onKeyUp, this);
17237         }
17238         if(this.forceSelection){
17239             this.inputEl().on('blur', this.doForce, this);
17240         }
17241         
17242         if(this.multiple){
17243             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17244             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17245         }
17246     },
17247     
17248     initTickableEvents: function()
17249     {   
17250         this.createList();
17251         
17252         if(this.hiddenName){
17253             
17254             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17255             
17256             this.hiddenField.dom.value =
17257                 this.hiddenValue !== undefined ? this.hiddenValue :
17258                 this.value !== undefined ? this.value : '';
17259
17260             // prevent input submission
17261             this.el.dom.removeAttribute('name');
17262             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17263              
17264              
17265         }
17266         
17267 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17268         
17269         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17270         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17271         if(this.triggerList){
17272             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17273         }
17274          
17275         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17276         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17277         
17278         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17279         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17280         
17281         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17282         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17283         
17284         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17285         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17286         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17287         
17288         this.okBtn.hide();
17289         this.cancelBtn.hide();
17290         
17291         var _this = this;
17292         
17293         (function(){
17294             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17295             _this.list.setWidth(lw);
17296         }).defer(100);
17297         
17298         this.list.on('mouseover', this.onViewOver, this);
17299         this.list.on('mousemove', this.onViewMove, this);
17300         
17301         this.list.on('scroll', this.onViewScroll, this);
17302         
17303         if(!this.tpl){
17304             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17305                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17306         }
17307
17308         this.view = new Roo.View(this.list, this.tpl, {
17309             singleSelect:true,
17310             tickable:true,
17311             parent:this,
17312             store: this.store,
17313             selectedClass: this.selectedClass
17314         });
17315         
17316         //this.view.wrapEl.setDisplayed(false);
17317         this.view.on('click', this.onViewClick, this);
17318         
17319         
17320         
17321         this.store.on('beforeload', this.onBeforeLoad, this);
17322         this.store.on('load', this.onLoad, this);
17323         this.store.on('loadexception', this.onLoadException, this);
17324         
17325         if(this.editable){
17326             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17327                 "up" : function(e){
17328                     this.inKeyMode = true;
17329                     this.selectPrev();
17330                 },
17331
17332                 "down" : function(e){
17333                     this.inKeyMode = true;
17334                     this.selectNext();
17335                 },
17336
17337                 "enter" : function(e){
17338                     if(this.fireEvent("specialkey", this, e)){
17339                         this.onViewClick(false);
17340                     }
17341                     
17342                     return true;
17343                 },
17344
17345                 "esc" : function(e){
17346                     this.onTickableFooterButtonClick(e, false, false);
17347                 },
17348
17349                 "tab" : function(e){
17350                     this.fireEvent("specialkey", this, e);
17351                     
17352                     this.onTickableFooterButtonClick(e, false, false);
17353                     
17354                     return true;
17355                 },
17356
17357                 scope : this,
17358
17359                 doRelay : function(e, fn, key){
17360                     if(this.scope.isExpanded()){
17361                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17362                     }
17363                     return true;
17364                 },
17365
17366                 forceKeyDown: true
17367             });
17368         }
17369         
17370         this.queryDelay = Math.max(this.queryDelay || 10,
17371                 this.mode == 'local' ? 10 : 250);
17372         
17373         
17374         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17375         
17376         if(this.typeAhead){
17377             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17378         }
17379         
17380         if(this.editable !== false){
17381             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17382         }
17383         
17384         this.indicator = this.indicatorEl();
17385         
17386         if(this.indicator){
17387             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17388             this.indicator.hide();
17389         }
17390         
17391     },
17392
17393     onDestroy : function(){
17394         if(this.view){
17395             this.view.setStore(null);
17396             this.view.el.removeAllListeners();
17397             this.view.el.remove();
17398             this.view.purgeListeners();
17399         }
17400         if(this.list){
17401             this.list.dom.innerHTML  = '';
17402         }
17403         
17404         if(this.store){
17405             this.store.un('beforeload', this.onBeforeLoad, this);
17406             this.store.un('load', this.onLoad, this);
17407             this.store.un('loadexception', this.onLoadException, this);
17408         }
17409         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17410     },
17411
17412     // private
17413     fireKey : function(e){
17414         if(e.isNavKeyPress() && !this.list.isVisible()){
17415             this.fireEvent("specialkey", this, e);
17416         }
17417     },
17418
17419     // private
17420     onResize: function(w, h)
17421     {
17422         
17423         
17424 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17425 //        
17426 //        if(typeof w != 'number'){
17427 //            // we do not handle it!?!?
17428 //            return;
17429 //        }
17430 //        var tw = this.trigger.getWidth();
17431 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17432 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17433 //        var x = w - tw;
17434 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17435 //            
17436 //        //this.trigger.setStyle('left', x+'px');
17437 //        
17438 //        if(this.list && this.listWidth === undefined){
17439 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17440 //            this.list.setWidth(lw);
17441 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17442 //        }
17443         
17444     
17445         
17446     },
17447
17448     /**
17449      * Allow or prevent the user from directly editing the field text.  If false is passed,
17450      * the user will only be able to select from the items defined in the dropdown list.  This method
17451      * is the runtime equivalent of setting the 'editable' config option at config time.
17452      * @param {Boolean} value True to allow the user to directly edit the field text
17453      */
17454     setEditable : function(value){
17455         if(value == this.editable){
17456             return;
17457         }
17458         this.editable = value;
17459         if(!value){
17460             this.inputEl().dom.setAttribute('readOnly', true);
17461             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17462             this.inputEl().addClass('x-combo-noedit');
17463         }else{
17464             this.inputEl().dom.removeAttribute('readOnly');
17465             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17466             this.inputEl().removeClass('x-combo-noedit');
17467         }
17468     },
17469
17470     // private
17471     
17472     onBeforeLoad : function(combo,opts){
17473         if(!this.hasFocus){
17474             return;
17475         }
17476          if (!opts.add) {
17477             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17478          }
17479         this.restrictHeight();
17480         this.selectedIndex = -1;
17481     },
17482
17483     // private
17484     onLoad : function(){
17485         
17486         this.hasQuery = false;
17487         
17488         if(!this.hasFocus){
17489             return;
17490         }
17491         
17492         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17493             this.loading.hide();
17494         }
17495         
17496         if(this.store.getCount() > 0){
17497             
17498             this.expand();
17499             this.restrictHeight();
17500             if(this.lastQuery == this.allQuery){
17501                 if(this.editable && !this.tickable){
17502                     this.inputEl().dom.select();
17503                 }
17504                 
17505                 if(
17506                     !this.selectByValue(this.value, true) &&
17507                     this.autoFocus && 
17508                     (
17509                         !this.store.lastOptions ||
17510                         typeof(this.store.lastOptions.add) == 'undefined' || 
17511                         this.store.lastOptions.add != true
17512                     )
17513                 ){
17514                     this.select(0, true);
17515                 }
17516             }else{
17517                 if(this.autoFocus){
17518                     this.selectNext();
17519                 }
17520                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17521                     this.taTask.delay(this.typeAheadDelay);
17522                 }
17523             }
17524         }else{
17525             this.onEmptyResults();
17526         }
17527         
17528         //this.el.focus();
17529     },
17530     // private
17531     onLoadException : function()
17532     {
17533         this.hasQuery = false;
17534         
17535         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17536             this.loading.hide();
17537         }
17538         
17539         if(this.tickable && this.editable){
17540             return;
17541         }
17542         
17543         this.collapse();
17544         // only causes errors at present
17545         //Roo.log(this.store.reader.jsonData);
17546         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17547             // fixme
17548             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17549         //}
17550         
17551         
17552     },
17553     // private
17554     onTypeAhead : function(){
17555         if(this.store.getCount() > 0){
17556             var r = this.store.getAt(0);
17557             var newValue = r.data[this.displayField];
17558             var len = newValue.length;
17559             var selStart = this.getRawValue().length;
17560             
17561             if(selStart != len){
17562                 this.setRawValue(newValue);
17563                 this.selectText(selStart, newValue.length);
17564             }
17565         }
17566     },
17567
17568     // private
17569     onSelect : function(record, index){
17570         
17571         if(this.fireEvent('beforeselect', this, record, index) !== false){
17572         
17573             this.setFromData(index > -1 ? record.data : false);
17574             
17575             this.collapse();
17576             this.fireEvent('select', this, record, index);
17577         }
17578     },
17579
17580     /**
17581      * Returns the currently selected field value or empty string if no value is set.
17582      * @return {String} value The selected value
17583      */
17584     getValue : function()
17585     {
17586         if(Roo.isIOS && this.useNativeIOS){
17587             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17588         }
17589         
17590         if(this.multiple){
17591             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17592         }
17593         
17594         if(this.valueField){
17595             return typeof this.value != 'undefined' ? this.value : '';
17596         }else{
17597             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17598         }
17599     },
17600     
17601     getRawValue : function()
17602     {
17603         if(Roo.isIOS && this.useNativeIOS){
17604             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17605         }
17606         
17607         var v = this.inputEl().getValue();
17608         
17609         return v;
17610     },
17611
17612     /**
17613      * Clears any text/value currently set in the field
17614      */
17615     clearValue : function(){
17616         
17617         if(this.hiddenField){
17618             this.hiddenField.dom.value = '';
17619         }
17620         this.value = '';
17621         this.setRawValue('');
17622         this.lastSelectionText = '';
17623         this.lastData = false;
17624         
17625         var close = this.closeTriggerEl();
17626         
17627         if(close){
17628             close.hide();
17629         }
17630         
17631         this.validate();
17632         
17633     },
17634
17635     /**
17636      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17637      * will be displayed in the field.  If the value does not match the data value of an existing item,
17638      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17639      * Otherwise the field will be blank (although the value will still be set).
17640      * @param {String} value The value to match
17641      */
17642     setValue : function(v)
17643     {
17644         if(Roo.isIOS && this.useNativeIOS){
17645             this.setIOSValue(v);
17646             return;
17647         }
17648         
17649         if(this.multiple){
17650             this.syncValue();
17651             return;
17652         }
17653         
17654         var text = v;
17655         if(this.valueField){
17656             var r = this.findRecord(this.valueField, v);
17657             if(r){
17658                 text = r.data[this.displayField];
17659             }else if(this.valueNotFoundText !== undefined){
17660                 text = this.valueNotFoundText;
17661             }
17662         }
17663         this.lastSelectionText = text;
17664         if(this.hiddenField){
17665             this.hiddenField.dom.value = v;
17666         }
17667         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17668         this.value = v;
17669         
17670         var close = this.closeTriggerEl();
17671         
17672         if(close){
17673             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17674         }
17675         
17676         this.validate();
17677     },
17678     /**
17679      * @property {Object} the last set data for the element
17680      */
17681     
17682     lastData : false,
17683     /**
17684      * Sets the value of the field based on a object which is related to the record format for the store.
17685      * @param {Object} value the value to set as. or false on reset?
17686      */
17687     setFromData : function(o){
17688         
17689         if(this.multiple){
17690             this.addItem(o);
17691             return;
17692         }
17693             
17694         var dv = ''; // display value
17695         var vv = ''; // value value..
17696         this.lastData = o;
17697         if (this.displayField) {
17698             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17699         } else {
17700             // this is an error condition!!!
17701             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17702         }
17703         
17704         if(this.valueField){
17705             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17706         }
17707         
17708         var close = this.closeTriggerEl();
17709         
17710         if(close){
17711             if(dv.length || vv * 1 > 0){
17712                 close.show() ;
17713                 this.blockFocus=true;
17714             } else {
17715                 close.hide();
17716             }             
17717         }
17718         
17719         if(this.hiddenField){
17720             this.hiddenField.dom.value = vv;
17721             
17722             this.lastSelectionText = dv;
17723             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17724             this.value = vv;
17725             return;
17726         }
17727         // no hidden field.. - we store the value in 'value', but still display
17728         // display field!!!!
17729         this.lastSelectionText = dv;
17730         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17731         this.value = vv;
17732         
17733         
17734         
17735     },
17736     // private
17737     reset : function(){
17738         // overridden so that last data is reset..
17739         
17740         if(this.multiple){
17741             this.clearItem();
17742             return;
17743         }
17744         
17745         this.setValue(this.originalValue);
17746         //this.clearInvalid();
17747         this.lastData = false;
17748         if (this.view) {
17749             this.view.clearSelections();
17750         }
17751         
17752         this.validate();
17753     },
17754     // private
17755     findRecord : function(prop, value){
17756         var record;
17757         if(this.store.getCount() > 0){
17758             this.store.each(function(r){
17759                 if(r.data[prop] == value){
17760                     record = r;
17761                     return false;
17762                 }
17763                 return true;
17764             });
17765         }
17766         return record;
17767     },
17768     
17769     getName: function()
17770     {
17771         // returns hidden if it's set..
17772         if (!this.rendered) {return ''};
17773         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17774         
17775     },
17776     // private
17777     onViewMove : function(e, t){
17778         this.inKeyMode = false;
17779     },
17780
17781     // private
17782     onViewOver : function(e, t){
17783         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17784             return;
17785         }
17786         var item = this.view.findItemFromChild(t);
17787         
17788         if(item){
17789             var index = this.view.indexOf(item);
17790             this.select(index, false);
17791         }
17792     },
17793
17794     // private
17795     onViewClick : function(view, doFocus, el, e)
17796     {
17797         var index = this.view.getSelectedIndexes()[0];
17798         
17799         var r = this.store.getAt(index);
17800         
17801         if(this.tickable){
17802             
17803             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17804                 return;
17805             }
17806             
17807             var rm = false;
17808             var _this = this;
17809             
17810             Roo.each(this.tickItems, function(v,k){
17811                 
17812                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17813                     Roo.log(v);
17814                     _this.tickItems.splice(k, 1);
17815                     
17816                     if(typeof(e) == 'undefined' && view == false){
17817                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17818                     }
17819                     
17820                     rm = true;
17821                     return;
17822                 }
17823             });
17824             
17825             if(rm){
17826                 return;
17827             }
17828             
17829             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17830                 this.tickItems.push(r.data);
17831             }
17832             
17833             if(typeof(e) == 'undefined' && view == false){
17834                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17835             }
17836                     
17837             return;
17838         }
17839         
17840         if(r){
17841             this.onSelect(r, index);
17842         }
17843         if(doFocus !== false && !this.blockFocus){
17844             this.inputEl().focus();
17845         }
17846     },
17847
17848     // private
17849     restrictHeight : function(){
17850         //this.innerList.dom.style.height = '';
17851         //var inner = this.innerList.dom;
17852         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17853         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17854         //this.list.beginUpdate();
17855         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17856         this.list.alignTo(this.inputEl(), this.listAlign);
17857         this.list.alignTo(this.inputEl(), this.listAlign);
17858         //this.list.endUpdate();
17859     },
17860
17861     // private
17862     onEmptyResults : function(){
17863         
17864         if(this.tickable && this.editable){
17865             this.hasFocus = false;
17866             this.restrictHeight();
17867             return;
17868         }
17869         
17870         this.collapse();
17871     },
17872
17873     /**
17874      * Returns true if the dropdown list is expanded, else false.
17875      */
17876     isExpanded : function(){
17877         return this.list.isVisible();
17878     },
17879
17880     /**
17881      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17882      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17883      * @param {String} value The data value of the item to select
17884      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17885      * selected item if it is not currently in view (defaults to true)
17886      * @return {Boolean} True if the value matched an item in the list, else false
17887      */
17888     selectByValue : function(v, scrollIntoView){
17889         if(v !== undefined && v !== null){
17890             var r = this.findRecord(this.valueField || this.displayField, v);
17891             if(r){
17892                 this.select(this.store.indexOf(r), scrollIntoView);
17893                 return true;
17894             }
17895         }
17896         return false;
17897     },
17898
17899     /**
17900      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17901      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17902      * @param {Number} index The zero-based index of the list item to select
17903      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17904      * selected item if it is not currently in view (defaults to true)
17905      */
17906     select : function(index, scrollIntoView){
17907         this.selectedIndex = index;
17908         this.view.select(index);
17909         if(scrollIntoView !== false){
17910             var el = this.view.getNode(index);
17911             /*
17912              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17913              */
17914             if(el){
17915                 this.list.scrollChildIntoView(el, false);
17916             }
17917         }
17918     },
17919
17920     // private
17921     selectNext : function(){
17922         var ct = this.store.getCount();
17923         if(ct > 0){
17924             if(this.selectedIndex == -1){
17925                 this.select(0);
17926             }else if(this.selectedIndex < ct-1){
17927                 this.select(this.selectedIndex+1);
17928             }
17929         }
17930     },
17931
17932     // private
17933     selectPrev : function(){
17934         var ct = this.store.getCount();
17935         if(ct > 0){
17936             if(this.selectedIndex == -1){
17937                 this.select(0);
17938             }else if(this.selectedIndex != 0){
17939                 this.select(this.selectedIndex-1);
17940             }
17941         }
17942     },
17943
17944     // private
17945     onKeyUp : function(e){
17946         if(this.editable !== false && !e.isSpecialKey()){
17947             this.lastKey = e.getKey();
17948             this.dqTask.delay(this.queryDelay);
17949         }
17950     },
17951
17952     // private
17953     validateBlur : function(){
17954         return !this.list || !this.list.isVisible();   
17955     },
17956
17957     // private
17958     initQuery : function(){
17959         
17960         var v = this.getRawValue();
17961         
17962         if(this.tickable && this.editable){
17963             v = this.tickableInputEl().getValue();
17964         }
17965         
17966         this.doQuery(v);
17967     },
17968
17969     // private
17970     doForce : function(){
17971         if(this.inputEl().dom.value.length > 0){
17972             this.inputEl().dom.value =
17973                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17974              
17975         }
17976     },
17977
17978     /**
17979      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17980      * query allowing the query action to be canceled if needed.
17981      * @param {String} query The SQL query to execute
17982      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17983      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17984      * saved in the current store (defaults to false)
17985      */
17986     doQuery : function(q, forceAll){
17987         
17988         if(q === undefined || q === null){
17989             q = '';
17990         }
17991         var qe = {
17992             query: q,
17993             forceAll: forceAll,
17994             combo: this,
17995             cancel:false
17996         };
17997         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17998             return false;
17999         }
18000         q = qe.query;
18001         
18002         forceAll = qe.forceAll;
18003         if(forceAll === true || (q.length >= this.minChars)){
18004             
18005             this.hasQuery = true;
18006             
18007             if(this.lastQuery != q || this.alwaysQuery){
18008                 this.lastQuery = q;
18009                 if(this.mode == 'local'){
18010                     this.selectedIndex = -1;
18011                     if(forceAll){
18012                         this.store.clearFilter();
18013                     }else{
18014                         
18015                         if(this.specialFilter){
18016                             this.fireEvent('specialfilter', this);
18017                             this.onLoad();
18018                             return;
18019                         }
18020                         
18021                         this.store.filter(this.displayField, q);
18022                     }
18023                     
18024                     this.store.fireEvent("datachanged", this.store);
18025                     
18026                     this.onLoad();
18027                     
18028                     
18029                 }else{
18030                     
18031                     this.store.baseParams[this.queryParam] = q;
18032                     
18033                     var options = {params : this.getParams(q)};
18034                     
18035                     if(this.loadNext){
18036                         options.add = true;
18037                         options.params.start = this.page * this.pageSize;
18038                     }
18039                     
18040                     this.store.load(options);
18041                     
18042                     /*
18043                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18044                      *  we should expand the list on onLoad
18045                      *  so command out it
18046                      */
18047 //                    this.expand();
18048                 }
18049             }else{
18050                 this.selectedIndex = -1;
18051                 this.onLoad();   
18052             }
18053         }
18054         
18055         this.loadNext = false;
18056     },
18057     
18058     // private
18059     getParams : function(q){
18060         var p = {};
18061         //p[this.queryParam] = q;
18062         
18063         if(this.pageSize){
18064             p.start = 0;
18065             p.limit = this.pageSize;
18066         }
18067         return p;
18068     },
18069
18070     /**
18071      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18072      */
18073     collapse : function(){
18074         if(!this.isExpanded()){
18075             return;
18076         }
18077         
18078         this.list.hide();
18079         
18080         this.hasFocus = false;
18081         
18082         if(this.tickable){
18083             this.okBtn.hide();
18084             this.cancelBtn.hide();
18085             this.trigger.show();
18086             
18087             if(this.editable){
18088                 this.tickableInputEl().dom.value = '';
18089                 this.tickableInputEl().blur();
18090             }
18091             
18092         }
18093         
18094         Roo.get(document).un('mousedown', this.collapseIf, this);
18095         Roo.get(document).un('mousewheel', this.collapseIf, this);
18096         if (!this.editable) {
18097             Roo.get(document).un('keydown', this.listKeyPress, this);
18098         }
18099         this.fireEvent('collapse', this);
18100         
18101         this.validate();
18102     },
18103
18104     // private
18105     collapseIf : function(e){
18106         var in_combo  = e.within(this.el);
18107         var in_list =  e.within(this.list);
18108         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18109         
18110         if (in_combo || in_list || is_list) {
18111             //e.stopPropagation();
18112             return;
18113         }
18114         
18115         if(this.tickable){
18116             this.onTickableFooterButtonClick(e, false, false);
18117         }
18118
18119         this.collapse();
18120         
18121     },
18122
18123     /**
18124      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18125      */
18126     expand : function(){
18127        
18128         if(this.isExpanded() || !this.hasFocus){
18129             return;
18130         }
18131         
18132         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18133         this.list.setWidth(lw);
18134         
18135         Roo.log('expand');
18136         
18137         this.list.show();
18138         
18139         this.restrictHeight();
18140         
18141         if(this.tickable){
18142             
18143             this.tickItems = Roo.apply([], this.item);
18144             
18145             this.okBtn.show();
18146             this.cancelBtn.show();
18147             this.trigger.hide();
18148             
18149             if(this.editable){
18150                 this.tickableInputEl().focus();
18151             }
18152             
18153         }
18154         
18155         Roo.get(document).on('mousedown', this.collapseIf, this);
18156         Roo.get(document).on('mousewheel', this.collapseIf, this);
18157         if (!this.editable) {
18158             Roo.get(document).on('keydown', this.listKeyPress, this);
18159         }
18160         
18161         this.fireEvent('expand', this);
18162     },
18163
18164     // private
18165     // Implements the default empty TriggerField.onTriggerClick function
18166     onTriggerClick : function(e)
18167     {
18168         Roo.log('trigger click');
18169         
18170         if(this.disabled || !this.triggerList){
18171             return;
18172         }
18173         
18174         this.page = 0;
18175         this.loadNext = false;
18176         
18177         if(this.isExpanded()){
18178             this.collapse();
18179             if (!this.blockFocus) {
18180                 this.inputEl().focus();
18181             }
18182             
18183         }else {
18184             this.hasFocus = true;
18185             if(this.triggerAction == 'all') {
18186                 this.doQuery(this.allQuery, true);
18187             } else {
18188                 this.doQuery(this.getRawValue());
18189             }
18190             if (!this.blockFocus) {
18191                 this.inputEl().focus();
18192             }
18193         }
18194     },
18195     
18196     onTickableTriggerClick : function(e)
18197     {
18198         if(this.disabled){
18199             return;
18200         }
18201         
18202         this.page = 0;
18203         this.loadNext = false;
18204         this.hasFocus = true;
18205         
18206         if(this.triggerAction == 'all') {
18207             this.doQuery(this.allQuery, true);
18208         } else {
18209             this.doQuery(this.getRawValue());
18210         }
18211     },
18212     
18213     onSearchFieldClick : function(e)
18214     {
18215         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18216             this.onTickableFooterButtonClick(e, false, false);
18217             return;
18218         }
18219         
18220         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18221             return;
18222         }
18223         
18224         this.page = 0;
18225         this.loadNext = false;
18226         this.hasFocus = true;
18227         
18228         if(this.triggerAction == 'all') {
18229             this.doQuery(this.allQuery, true);
18230         } else {
18231             this.doQuery(this.getRawValue());
18232         }
18233     },
18234     
18235     listKeyPress : function(e)
18236     {
18237         //Roo.log('listkeypress');
18238         // scroll to first matching element based on key pres..
18239         if (e.isSpecialKey()) {
18240             return false;
18241         }
18242         var k = String.fromCharCode(e.getKey()).toUpperCase();
18243         //Roo.log(k);
18244         var match  = false;
18245         var csel = this.view.getSelectedNodes();
18246         var cselitem = false;
18247         if (csel.length) {
18248             var ix = this.view.indexOf(csel[0]);
18249             cselitem  = this.store.getAt(ix);
18250             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18251                 cselitem = false;
18252             }
18253             
18254         }
18255         
18256         this.store.each(function(v) { 
18257             if (cselitem) {
18258                 // start at existing selection.
18259                 if (cselitem.id == v.id) {
18260                     cselitem = false;
18261                 }
18262                 return true;
18263             }
18264                 
18265             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18266                 match = this.store.indexOf(v);
18267                 return false;
18268             }
18269             return true;
18270         }, this);
18271         
18272         if (match === false) {
18273             return true; // no more action?
18274         }
18275         // scroll to?
18276         this.view.select(match);
18277         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18278         sn.scrollIntoView(sn.dom.parentNode, false);
18279     },
18280     
18281     onViewScroll : function(e, t){
18282         
18283         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){
18284             return;
18285         }
18286         
18287         this.hasQuery = true;
18288         
18289         this.loading = this.list.select('.loading', true).first();
18290         
18291         if(this.loading === null){
18292             this.list.createChild({
18293                 tag: 'div',
18294                 cls: 'loading roo-select2-more-results roo-select2-active',
18295                 html: 'Loading more results...'
18296             });
18297             
18298             this.loading = this.list.select('.loading', true).first();
18299             
18300             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18301             
18302             this.loading.hide();
18303         }
18304         
18305         this.loading.show();
18306         
18307         var _combo = this;
18308         
18309         this.page++;
18310         this.loadNext = true;
18311         
18312         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18313         
18314         return;
18315     },
18316     
18317     addItem : function(o)
18318     {   
18319         var dv = ''; // display value
18320         
18321         if (this.displayField) {
18322             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18323         } else {
18324             // this is an error condition!!!
18325             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18326         }
18327         
18328         if(!dv.length){
18329             return;
18330         }
18331         
18332         var choice = this.choices.createChild({
18333             tag: 'li',
18334             cls: 'roo-select2-search-choice',
18335             cn: [
18336                 {
18337                     tag: 'div',
18338                     html: dv
18339                 },
18340                 {
18341                     tag: 'a',
18342                     href: '#',
18343                     cls: 'roo-select2-search-choice-close fa fa-times',
18344                     tabindex: '-1'
18345                 }
18346             ]
18347             
18348         }, this.searchField);
18349         
18350         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18351         
18352         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18353         
18354         this.item.push(o);
18355         
18356         this.lastData = o;
18357         
18358         this.syncValue();
18359         
18360         this.inputEl().dom.value = '';
18361         
18362         this.validate();
18363     },
18364     
18365     onRemoveItem : function(e, _self, o)
18366     {
18367         e.preventDefault();
18368         
18369         this.lastItem = Roo.apply([], this.item);
18370         
18371         var index = this.item.indexOf(o.data) * 1;
18372         
18373         if( index < 0){
18374             Roo.log('not this item?!');
18375             return;
18376         }
18377         
18378         this.item.splice(index, 1);
18379         o.item.remove();
18380         
18381         this.syncValue();
18382         
18383         this.fireEvent('remove', this, e);
18384         
18385         this.validate();
18386         
18387     },
18388     
18389     syncValue : function()
18390     {
18391         if(!this.item.length){
18392             this.clearValue();
18393             return;
18394         }
18395             
18396         var value = [];
18397         var _this = this;
18398         Roo.each(this.item, function(i){
18399             if(_this.valueField){
18400                 value.push(i[_this.valueField]);
18401                 return;
18402             }
18403
18404             value.push(i);
18405         });
18406
18407         this.value = value.join(',');
18408
18409         if(this.hiddenField){
18410             this.hiddenField.dom.value = this.value;
18411         }
18412         
18413         this.store.fireEvent("datachanged", this.store);
18414         
18415         this.validate();
18416     },
18417     
18418     clearItem : function()
18419     {
18420         if(!this.multiple){
18421             return;
18422         }
18423         
18424         this.item = [];
18425         
18426         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18427            c.remove();
18428         });
18429         
18430         this.syncValue();
18431         
18432         this.validate();
18433         
18434         if(this.tickable && !Roo.isTouch){
18435             this.view.refresh();
18436         }
18437     },
18438     
18439     inputEl: function ()
18440     {
18441         if(Roo.isIOS && this.useNativeIOS){
18442             return this.el.select('select.roo-ios-select', true).first();
18443         }
18444         
18445         if(Roo.isTouch && this.mobileTouchView){
18446             return this.el.select('input.form-control',true).first();
18447         }
18448         
18449         if(this.tickable){
18450             return this.searchField;
18451         }
18452         
18453         return this.el.select('input.form-control',true).first();
18454     },
18455     
18456     onTickableFooterButtonClick : function(e, btn, el)
18457     {
18458         e.preventDefault();
18459         
18460         this.lastItem = Roo.apply([], this.item);
18461         
18462         if(btn && btn.name == 'cancel'){
18463             this.tickItems = Roo.apply([], this.item);
18464             this.collapse();
18465             return;
18466         }
18467         
18468         this.clearItem();
18469         
18470         var _this = this;
18471         
18472         Roo.each(this.tickItems, function(o){
18473             _this.addItem(o);
18474         });
18475         
18476         this.collapse();
18477         
18478     },
18479     
18480     validate : function()
18481     {
18482         if(this.getVisibilityEl().hasClass('hidden')){
18483             return true;
18484         }
18485         
18486         var v = this.getRawValue();
18487         
18488         if(this.multiple){
18489             v = this.getValue();
18490         }
18491         
18492         if(this.disabled || this.allowBlank || v.length){
18493             this.markValid();
18494             return true;
18495         }
18496         
18497         this.markInvalid();
18498         return false;
18499     },
18500     
18501     tickableInputEl : function()
18502     {
18503         if(!this.tickable || !this.editable){
18504             return this.inputEl();
18505         }
18506         
18507         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18508     },
18509     
18510     
18511     getAutoCreateTouchView : function()
18512     {
18513         var id = Roo.id();
18514         
18515         var cfg = {
18516             cls: 'form-group' //input-group
18517         };
18518         
18519         var input =  {
18520             tag: 'input',
18521             id : id,
18522             type : this.inputType,
18523             cls : 'form-control x-combo-noedit',
18524             autocomplete: 'new-password',
18525             placeholder : this.placeholder || '',
18526             readonly : true
18527         };
18528         
18529         if (this.name) {
18530             input.name = this.name;
18531         }
18532         
18533         if (this.size) {
18534             input.cls += ' input-' + this.size;
18535         }
18536         
18537         if (this.disabled) {
18538             input.disabled = true;
18539         }
18540         
18541         var inputblock = {
18542             cls : 'roo-combobox-wrap',
18543             cn : [
18544                 input
18545             ]
18546         };
18547         
18548         if(this.before){
18549             inputblock.cls += ' input-group';
18550             
18551             inputblock.cn.unshift({
18552                 tag :'span',
18553                 cls : 'input-group-addon input-group-prepend input-group-text',
18554                 html : this.before
18555             });
18556         }
18557         
18558         if(this.removable && !this.multiple){
18559             inputblock.cls += ' roo-removable';
18560             
18561             inputblock.cn.push({
18562                 tag: 'button',
18563                 html : 'x',
18564                 cls : 'roo-combo-removable-btn close'
18565             });
18566         }
18567
18568         if(this.hasFeedback && !this.allowBlank){
18569             
18570             inputblock.cls += ' has-feedback';
18571             
18572             inputblock.cn.push({
18573                 tag: 'span',
18574                 cls: 'glyphicon form-control-feedback'
18575             });
18576             
18577         }
18578         
18579         if (this.after) {
18580             
18581             inputblock.cls += (this.before) ? '' : ' input-group';
18582             
18583             inputblock.cn.push({
18584                 tag :'span',
18585                 cls : 'input-group-addon input-group-append input-group-text',
18586                 html : this.after
18587             });
18588         }
18589
18590         
18591         var ibwrap = inputblock;
18592         
18593         if(this.multiple){
18594             ibwrap = {
18595                 tag: 'ul',
18596                 cls: 'roo-select2-choices',
18597                 cn:[
18598                     {
18599                         tag: 'li',
18600                         cls: 'roo-select2-search-field',
18601                         cn: [
18602
18603                             inputblock
18604                         ]
18605                     }
18606                 ]
18607             };
18608         
18609             
18610         }
18611         
18612         var combobox = {
18613             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18614             cn: [
18615                 {
18616                     tag: 'input',
18617                     type : 'hidden',
18618                     cls: 'form-hidden-field'
18619                 },
18620                 ibwrap
18621             ]
18622         };
18623         
18624         if(!this.multiple && this.showToggleBtn){
18625             
18626             var caret = {
18627                 cls: 'caret'
18628             };
18629             
18630             if (this.caret != false) {
18631                 caret = {
18632                      tag: 'i',
18633                      cls: 'fa fa-' + this.caret
18634                 };
18635                 
18636             }
18637             
18638             combobox.cn.push({
18639                 tag :'span',
18640                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18641                 cn : [
18642                     Roo.bootstrap.version == 3 ? caret : '',
18643                     {
18644                         tag: 'span',
18645                         cls: 'combobox-clear',
18646                         cn  : [
18647                             {
18648                                 tag : 'i',
18649                                 cls: 'icon-remove'
18650                             }
18651                         ]
18652                     }
18653                 ]
18654
18655             })
18656         }
18657         
18658         if(this.multiple){
18659             combobox.cls += ' roo-select2-container-multi';
18660         }
18661         
18662         var required =  this.allowBlank ?  {
18663                     tag : 'i',
18664                     style: 'display: none'
18665                 } : {
18666                    tag : 'i',
18667                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18668                    tooltip : 'This field is required'
18669                 };
18670         
18671         var align = this.labelAlign || this.parentLabelAlign();
18672         
18673         if (align ==='left' && this.fieldLabel.length) {
18674
18675             cfg.cn = [
18676                 required,
18677                 {
18678                     tag: 'label',
18679                     cls : 'control-label col-form-label',
18680                     html : this.fieldLabel
18681
18682                 },
18683                 {
18684                     cls : 'roo-combobox-wrap ', 
18685                     cn: [
18686                         combobox
18687                     ]
18688                 }
18689             ];
18690             
18691             var labelCfg = cfg.cn[1];
18692             var contentCfg = cfg.cn[2];
18693             
18694
18695             if(this.indicatorpos == 'right'){
18696                 cfg.cn = [
18697                     {
18698                         tag: 'label',
18699                         'for' :  id,
18700                         cls : 'control-label col-form-label',
18701                         cn : [
18702                             {
18703                                 tag : 'span',
18704                                 html : this.fieldLabel
18705                             },
18706                             required
18707                         ]
18708                     },
18709                     {
18710                         cls : "roo-combobox-wrap ",
18711                         cn: [
18712                             combobox
18713                         ]
18714                     }
18715
18716                 ];
18717                 
18718                 labelCfg = cfg.cn[0];
18719                 contentCfg = cfg.cn[1];
18720             }
18721             
18722            
18723             
18724             if(this.labelWidth > 12){
18725                 labelCfg.style = "width: " + this.labelWidth + 'px';
18726             }
18727            
18728             if(this.labelWidth < 13 && this.labelmd == 0){
18729                 this.labelmd = this.labelWidth;
18730             }
18731             
18732             if(this.labellg > 0){
18733                 labelCfg.cls += ' col-lg-' + this.labellg;
18734                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18735             }
18736             
18737             if(this.labelmd > 0){
18738                 labelCfg.cls += ' col-md-' + this.labelmd;
18739                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18740             }
18741             
18742             if(this.labelsm > 0){
18743                 labelCfg.cls += ' col-sm-' + this.labelsm;
18744                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18745             }
18746             
18747             if(this.labelxs > 0){
18748                 labelCfg.cls += ' col-xs-' + this.labelxs;
18749                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18750             }
18751                 
18752                 
18753         } else if ( this.fieldLabel.length) {
18754             cfg.cn = [
18755                required,
18756                 {
18757                     tag: 'label',
18758                     cls : 'control-label',
18759                     html : this.fieldLabel
18760
18761                 },
18762                 {
18763                     cls : '', 
18764                     cn: [
18765                         combobox
18766                     ]
18767                 }
18768             ];
18769             
18770             if(this.indicatorpos == 'right'){
18771                 cfg.cn = [
18772                     {
18773                         tag: 'label',
18774                         cls : 'control-label',
18775                         html : this.fieldLabel,
18776                         cn : [
18777                             required
18778                         ]
18779                     },
18780                     {
18781                         cls : '', 
18782                         cn: [
18783                             combobox
18784                         ]
18785                     }
18786                 ];
18787             }
18788         } else {
18789             cfg.cn = combobox;    
18790         }
18791         
18792         
18793         var settings = this;
18794         
18795         ['xs','sm','md','lg'].map(function(size){
18796             if (settings[size]) {
18797                 cfg.cls += ' col-' + size + '-' + settings[size];
18798             }
18799         });
18800         
18801         return cfg;
18802     },
18803     
18804     initTouchView : function()
18805     {
18806         this.renderTouchView();
18807         
18808         this.touchViewEl.on('scroll', function(){
18809             this.el.dom.scrollTop = 0;
18810         }, this);
18811         
18812         this.originalValue = this.getValue();
18813         
18814         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18815         
18816         this.inputEl().on("click", this.showTouchView, this);
18817         if (this.triggerEl) {
18818             this.triggerEl.on("click", this.showTouchView, this);
18819         }
18820         
18821         
18822         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18823         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18824         
18825         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18826         
18827         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18828         this.store.on('load', this.onTouchViewLoad, this);
18829         this.store.on('loadexception', this.onTouchViewLoadException, this);
18830         
18831         if(this.hiddenName){
18832             
18833             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18834             
18835             this.hiddenField.dom.value =
18836                 this.hiddenValue !== undefined ? this.hiddenValue :
18837                 this.value !== undefined ? this.value : '';
18838         
18839             this.el.dom.removeAttribute('name');
18840             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18841         }
18842         
18843         if(this.multiple){
18844             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18845             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18846         }
18847         
18848         if(this.removable && !this.multiple){
18849             var close = this.closeTriggerEl();
18850             if(close){
18851                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18852                 close.on('click', this.removeBtnClick, this, close);
18853             }
18854         }
18855         /*
18856          * fix the bug in Safari iOS8
18857          */
18858         this.inputEl().on("focus", function(e){
18859             document.activeElement.blur();
18860         }, this);
18861         
18862         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18863         
18864         return;
18865         
18866         
18867     },
18868     
18869     renderTouchView : function()
18870     {
18871         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18872         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18873         
18874         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18875         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18876         
18877         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18878         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18879         this.touchViewBodyEl.setStyle('overflow', 'auto');
18880         
18881         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18882         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18883         
18884         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18885         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18886         
18887     },
18888     
18889     showTouchView : function()
18890     {
18891         if(this.disabled){
18892             return;
18893         }
18894         
18895         this.touchViewHeaderEl.hide();
18896
18897         if(this.modalTitle.length){
18898             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18899             this.touchViewHeaderEl.show();
18900         }
18901
18902         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18903         this.touchViewEl.show();
18904
18905         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18906         
18907         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18908         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18909
18910         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18911
18912         if(this.modalTitle.length){
18913             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18914         }
18915         
18916         this.touchViewBodyEl.setHeight(bodyHeight);
18917
18918         if(this.animate){
18919             var _this = this;
18920             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18921         }else{
18922             this.touchViewEl.addClass(['in','show']);
18923         }
18924         
18925         if(this._touchViewMask){
18926             Roo.get(document.body).addClass("x-body-masked");
18927             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18928             this._touchViewMask.setStyle('z-index', 10000);
18929             this._touchViewMask.addClass('show');
18930         }
18931         
18932         this.doTouchViewQuery();
18933         
18934     },
18935     
18936     hideTouchView : function()
18937     {
18938         this.touchViewEl.removeClass(['in','show']);
18939
18940         if(this.animate){
18941             var _this = this;
18942             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18943         }else{
18944             this.touchViewEl.setStyle('display', 'none');
18945         }
18946         
18947         if(this._touchViewMask){
18948             this._touchViewMask.removeClass('show');
18949             Roo.get(document.body).removeClass("x-body-masked");
18950         }
18951     },
18952     
18953     setTouchViewValue : function()
18954     {
18955         if(this.multiple){
18956             this.clearItem();
18957         
18958             var _this = this;
18959
18960             Roo.each(this.tickItems, function(o){
18961                 this.addItem(o);
18962             }, this);
18963         }
18964         
18965         this.hideTouchView();
18966     },
18967     
18968     doTouchViewQuery : function()
18969     {
18970         var qe = {
18971             query: '',
18972             forceAll: true,
18973             combo: this,
18974             cancel:false
18975         };
18976         
18977         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18978             return false;
18979         }
18980         
18981         if(!this.alwaysQuery || this.mode == 'local'){
18982             this.onTouchViewLoad();
18983             return;
18984         }
18985         
18986         this.store.load();
18987     },
18988     
18989     onTouchViewBeforeLoad : function(combo,opts)
18990     {
18991         return;
18992     },
18993
18994     // private
18995     onTouchViewLoad : function()
18996     {
18997         if(this.store.getCount() < 1){
18998             this.onTouchViewEmptyResults();
18999             return;
19000         }
19001         
19002         this.clearTouchView();
19003         
19004         var rawValue = this.getRawValue();
19005         
19006         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19007         
19008         this.tickItems = [];
19009         
19010         this.store.data.each(function(d, rowIndex){
19011             var row = this.touchViewListGroup.createChild(template);
19012             
19013             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19014                 row.addClass(d.data.cls);
19015             }
19016             
19017             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19018                 var cfg = {
19019                     data : d.data,
19020                     html : d.data[this.displayField]
19021                 };
19022                 
19023                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19024                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19025                 }
19026             }
19027             row.removeClass('selected');
19028             if(!this.multiple && this.valueField &&
19029                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19030             {
19031                 // radio buttons..
19032                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19033                 row.addClass('selected');
19034             }
19035             
19036             if(this.multiple && this.valueField &&
19037                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19038             {
19039                 
19040                 // checkboxes...
19041                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19042                 this.tickItems.push(d.data);
19043             }
19044             
19045             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19046             
19047         }, this);
19048         
19049         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19050         
19051         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19052
19053         if(this.modalTitle.length){
19054             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19055         }
19056
19057         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19058         
19059         if(this.mobile_restrict_height && listHeight < bodyHeight){
19060             this.touchViewBodyEl.setHeight(listHeight);
19061         }
19062         
19063         var _this = this;
19064         
19065         if(firstChecked && listHeight > bodyHeight){
19066             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19067         }
19068         
19069     },
19070     
19071     onTouchViewLoadException : function()
19072     {
19073         this.hideTouchView();
19074     },
19075     
19076     onTouchViewEmptyResults : function()
19077     {
19078         this.clearTouchView();
19079         
19080         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19081         
19082         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19083         
19084     },
19085     
19086     clearTouchView : function()
19087     {
19088         this.touchViewListGroup.dom.innerHTML = '';
19089     },
19090     
19091     onTouchViewClick : function(e, el, o)
19092     {
19093         e.preventDefault();
19094         
19095         var row = o.row;
19096         var rowIndex = o.rowIndex;
19097         
19098         var r = this.store.getAt(rowIndex);
19099         
19100         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19101             
19102             if(!this.multiple){
19103                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19104                     c.dom.removeAttribute('checked');
19105                 }, this);
19106
19107                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19108
19109                 this.setFromData(r.data);
19110
19111                 var close = this.closeTriggerEl();
19112
19113                 if(close){
19114                     close.show();
19115                 }
19116
19117                 this.hideTouchView();
19118
19119                 this.fireEvent('select', this, r, rowIndex);
19120
19121                 return;
19122             }
19123
19124             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19125                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19126                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19127                 return;
19128             }
19129
19130             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19131             this.addItem(r.data);
19132             this.tickItems.push(r.data);
19133         }
19134     },
19135     
19136     getAutoCreateNativeIOS : function()
19137     {
19138         var cfg = {
19139             cls: 'form-group' //input-group,
19140         };
19141         
19142         var combobox =  {
19143             tag: 'select',
19144             cls : 'roo-ios-select'
19145         };
19146         
19147         if (this.name) {
19148             combobox.name = this.name;
19149         }
19150         
19151         if (this.disabled) {
19152             combobox.disabled = true;
19153         }
19154         
19155         var settings = this;
19156         
19157         ['xs','sm','md','lg'].map(function(size){
19158             if (settings[size]) {
19159                 cfg.cls += ' col-' + size + '-' + settings[size];
19160             }
19161         });
19162         
19163         cfg.cn = combobox;
19164         
19165         return cfg;
19166         
19167     },
19168     
19169     initIOSView : function()
19170     {
19171         this.store.on('load', this.onIOSViewLoad, this);
19172         
19173         return;
19174     },
19175     
19176     onIOSViewLoad : function()
19177     {
19178         if(this.store.getCount() < 1){
19179             return;
19180         }
19181         
19182         this.clearIOSView();
19183         
19184         if(this.allowBlank) {
19185             
19186             var default_text = '-- SELECT --';
19187             
19188             if(this.placeholder.length){
19189                 default_text = this.placeholder;
19190             }
19191             
19192             if(this.emptyTitle.length){
19193                 default_text += ' - ' + this.emptyTitle + ' -';
19194             }
19195             
19196             var opt = this.inputEl().createChild({
19197                 tag: 'option',
19198                 value : 0,
19199                 html : default_text
19200             });
19201             
19202             var o = {};
19203             o[this.valueField] = 0;
19204             o[this.displayField] = default_text;
19205             
19206             this.ios_options.push({
19207                 data : o,
19208                 el : opt
19209             });
19210             
19211         }
19212         
19213         this.store.data.each(function(d, rowIndex){
19214             
19215             var html = '';
19216             
19217             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19218                 html = d.data[this.displayField];
19219             }
19220             
19221             var value = '';
19222             
19223             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19224                 value = d.data[this.valueField];
19225             }
19226             
19227             var option = {
19228                 tag: 'option',
19229                 value : value,
19230                 html : html
19231             };
19232             
19233             if(this.value == d.data[this.valueField]){
19234                 option['selected'] = true;
19235             }
19236             
19237             var opt = this.inputEl().createChild(option);
19238             
19239             this.ios_options.push({
19240                 data : d.data,
19241                 el : opt
19242             });
19243             
19244         }, this);
19245         
19246         this.inputEl().on('change', function(){
19247            this.fireEvent('select', this);
19248         }, this);
19249         
19250     },
19251     
19252     clearIOSView: function()
19253     {
19254         this.inputEl().dom.innerHTML = '';
19255         
19256         this.ios_options = [];
19257     },
19258     
19259     setIOSValue: function(v)
19260     {
19261         this.value = v;
19262         
19263         if(!this.ios_options){
19264             return;
19265         }
19266         
19267         Roo.each(this.ios_options, function(opts){
19268            
19269            opts.el.dom.removeAttribute('selected');
19270            
19271            if(opts.data[this.valueField] != v){
19272                return;
19273            }
19274            
19275            opts.el.dom.setAttribute('selected', true);
19276            
19277         }, this);
19278     }
19279
19280     /** 
19281     * @cfg {Boolean} grow 
19282     * @hide 
19283     */
19284     /** 
19285     * @cfg {Number} growMin 
19286     * @hide 
19287     */
19288     /** 
19289     * @cfg {Number} growMax 
19290     * @hide 
19291     */
19292     /**
19293      * @hide
19294      * @method autoSize
19295      */
19296 });
19297
19298 Roo.apply(Roo.bootstrap.ComboBox,  {
19299     
19300     header : {
19301         tag: 'div',
19302         cls: 'modal-header',
19303         cn: [
19304             {
19305                 tag: 'h4',
19306                 cls: 'modal-title'
19307             }
19308         ]
19309     },
19310     
19311     body : {
19312         tag: 'div',
19313         cls: 'modal-body',
19314         cn: [
19315             {
19316                 tag: 'ul',
19317                 cls: 'list-group'
19318             }
19319         ]
19320     },
19321     
19322     listItemRadio : {
19323         tag: 'li',
19324         cls: 'list-group-item',
19325         cn: [
19326             {
19327                 tag: 'span',
19328                 cls: 'roo-combobox-list-group-item-value'
19329             },
19330             {
19331                 tag: 'div',
19332                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19333                 cn: [
19334                     {
19335                         tag: 'input',
19336                         type: 'radio'
19337                     },
19338                     {
19339                         tag: 'label'
19340                     }
19341                 ]
19342             }
19343         ]
19344     },
19345     
19346     listItemCheckbox : {
19347         tag: 'li',
19348         cls: 'list-group-item',
19349         cn: [
19350             {
19351                 tag: 'span',
19352                 cls: 'roo-combobox-list-group-item-value'
19353             },
19354             {
19355                 tag: 'div',
19356                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19357                 cn: [
19358                     {
19359                         tag: 'input',
19360                         type: 'checkbox'
19361                     },
19362                     {
19363                         tag: 'label'
19364                     }
19365                 ]
19366             }
19367         ]
19368     },
19369     
19370     emptyResult : {
19371         tag: 'div',
19372         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19373     },
19374     
19375     footer : {
19376         tag: 'div',
19377         cls: 'modal-footer',
19378         cn: [
19379             {
19380                 tag: 'div',
19381                 cls: 'row',
19382                 cn: [
19383                     {
19384                         tag: 'div',
19385                         cls: 'col-xs-6 text-left',
19386                         cn: {
19387                             tag: 'button',
19388                             cls: 'btn btn-danger roo-touch-view-cancel',
19389                             html: 'Cancel'
19390                         }
19391                     },
19392                     {
19393                         tag: 'div',
19394                         cls: 'col-xs-6 text-right',
19395                         cn: {
19396                             tag: 'button',
19397                             cls: 'btn btn-success roo-touch-view-ok',
19398                             html: 'OK'
19399                         }
19400                     }
19401                 ]
19402             }
19403         ]
19404         
19405     }
19406 });
19407
19408 Roo.apply(Roo.bootstrap.ComboBox,  {
19409     
19410     touchViewTemplate : {
19411         tag: 'div',
19412         cls: 'modal fade roo-combobox-touch-view',
19413         cn: [
19414             {
19415                 tag: 'div',
19416                 cls: 'modal-dialog',
19417                 style : 'position:fixed', // we have to fix position....
19418                 cn: [
19419                     {
19420                         tag: 'div',
19421                         cls: 'modal-content',
19422                         cn: [
19423                             Roo.bootstrap.ComboBox.header,
19424                             Roo.bootstrap.ComboBox.body,
19425                             Roo.bootstrap.ComboBox.footer
19426                         ]
19427                     }
19428                 ]
19429             }
19430         ]
19431     }
19432 });/*
19433  * Based on:
19434  * Ext JS Library 1.1.1
19435  * Copyright(c) 2006-2007, Ext JS, LLC.
19436  *
19437  * Originally Released Under LGPL - original licence link has changed is not relivant.
19438  *
19439  * Fork - LGPL
19440  * <script type="text/javascript">
19441  */
19442
19443 /**
19444  * @class Roo.View
19445  * @extends Roo.util.Observable
19446  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19447  * This class also supports single and multi selection modes. <br>
19448  * Create a data model bound view:
19449  <pre><code>
19450  var store = new Roo.data.Store(...);
19451
19452  var view = new Roo.View({
19453     el : "my-element",
19454     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19455  
19456     singleSelect: true,
19457     selectedClass: "ydataview-selected",
19458     store: store
19459  });
19460
19461  // listen for node click?
19462  view.on("click", function(vw, index, node, e){
19463  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19464  });
19465
19466  // load XML data
19467  dataModel.load("foobar.xml");
19468  </code></pre>
19469  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19470  * <br><br>
19471  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19472  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19473  * 
19474  * Note: old style constructor is still suported (container, template, config)
19475  * 
19476  * @constructor
19477  * Create a new View
19478  * @param {Object} config The config object
19479  * 
19480  */
19481 Roo.View = function(config, depreciated_tpl, depreciated_config){
19482     
19483     this.parent = false;
19484     
19485     if (typeof(depreciated_tpl) == 'undefined') {
19486         // new way.. - universal constructor.
19487         Roo.apply(this, config);
19488         this.el  = Roo.get(this.el);
19489     } else {
19490         // old format..
19491         this.el  = Roo.get(config);
19492         this.tpl = depreciated_tpl;
19493         Roo.apply(this, depreciated_config);
19494     }
19495     this.wrapEl  = this.el.wrap().wrap();
19496     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19497     
19498     
19499     if(typeof(this.tpl) == "string"){
19500         this.tpl = new Roo.Template(this.tpl);
19501     } else {
19502         // support xtype ctors..
19503         this.tpl = new Roo.factory(this.tpl, Roo);
19504     }
19505     
19506     
19507     this.tpl.compile();
19508     
19509     /** @private */
19510     this.addEvents({
19511         /**
19512          * @event beforeclick
19513          * Fires before a click is processed. Returns false to cancel the default action.
19514          * @param {Roo.View} this
19515          * @param {Number} index The index of the target node
19516          * @param {HTMLElement} node The target node
19517          * @param {Roo.EventObject} e The raw event object
19518          */
19519             "beforeclick" : true,
19520         /**
19521          * @event click
19522          * Fires when a template node is clicked.
19523          * @param {Roo.View} this
19524          * @param {Number} index The index of the target node
19525          * @param {HTMLElement} node The target node
19526          * @param {Roo.EventObject} e The raw event object
19527          */
19528             "click" : true,
19529         /**
19530          * @event dblclick
19531          * Fires when a template node is double clicked.
19532          * @param {Roo.View} this
19533          * @param {Number} index The index of the target node
19534          * @param {HTMLElement} node The target node
19535          * @param {Roo.EventObject} e The raw event object
19536          */
19537             "dblclick" : true,
19538         /**
19539          * @event contextmenu
19540          * Fires when a template node is right clicked.
19541          * @param {Roo.View} this
19542          * @param {Number} index The index of the target node
19543          * @param {HTMLElement} node The target node
19544          * @param {Roo.EventObject} e The raw event object
19545          */
19546             "contextmenu" : true,
19547         /**
19548          * @event selectionchange
19549          * Fires when the selected nodes change.
19550          * @param {Roo.View} this
19551          * @param {Array} selections Array of the selected nodes
19552          */
19553             "selectionchange" : true,
19554     
19555         /**
19556          * @event beforeselect
19557          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19558          * @param {Roo.View} this
19559          * @param {HTMLElement} node The node to be selected
19560          * @param {Array} selections Array of currently selected nodes
19561          */
19562             "beforeselect" : true,
19563         /**
19564          * @event preparedata
19565          * Fires on every row to render, to allow you to change the data.
19566          * @param {Roo.View} this
19567          * @param {Object} data to be rendered (change this)
19568          */
19569           "preparedata" : true
19570           
19571           
19572         });
19573
19574
19575
19576     this.el.on({
19577         "click": this.onClick,
19578         "dblclick": this.onDblClick,
19579         "contextmenu": this.onContextMenu,
19580         scope:this
19581     });
19582
19583     this.selections = [];
19584     this.nodes = [];
19585     this.cmp = new Roo.CompositeElementLite([]);
19586     if(this.store){
19587         this.store = Roo.factory(this.store, Roo.data);
19588         this.setStore(this.store, true);
19589     }
19590     
19591     if ( this.footer && this.footer.xtype) {
19592            
19593          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19594         
19595         this.footer.dataSource = this.store;
19596         this.footer.container = fctr;
19597         this.footer = Roo.factory(this.footer, Roo);
19598         fctr.insertFirst(this.el);
19599         
19600         // this is a bit insane - as the paging toolbar seems to detach the el..
19601 //        dom.parentNode.parentNode.parentNode
19602          // they get detached?
19603     }
19604     
19605     
19606     Roo.View.superclass.constructor.call(this);
19607     
19608     
19609 };
19610
19611 Roo.extend(Roo.View, Roo.util.Observable, {
19612     
19613      /**
19614      * @cfg {Roo.data.Store} store Data store to load data from.
19615      */
19616     store : false,
19617     
19618     /**
19619      * @cfg {String|Roo.Element} el The container element.
19620      */
19621     el : '',
19622     
19623     /**
19624      * @cfg {String|Roo.Template} tpl The template used by this View 
19625      */
19626     tpl : false,
19627     /**
19628      * @cfg {String} dataName the named area of the template to use as the data area
19629      *                          Works with domtemplates roo-name="name"
19630      */
19631     dataName: false,
19632     /**
19633      * @cfg {String} selectedClass The css class to add to selected nodes
19634      */
19635     selectedClass : "x-view-selected",
19636      /**
19637      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19638      */
19639     emptyText : "",
19640     
19641     /**
19642      * @cfg {String} text to display on mask (default Loading)
19643      */
19644     mask : false,
19645     /**
19646      * @cfg {Boolean} multiSelect Allow multiple selection
19647      */
19648     multiSelect : false,
19649     /**
19650      * @cfg {Boolean} singleSelect Allow single selection
19651      */
19652     singleSelect:  false,
19653     
19654     /**
19655      * @cfg {Boolean} toggleSelect - selecting 
19656      */
19657     toggleSelect : false,
19658     
19659     /**
19660      * @cfg {Boolean} tickable - selecting 
19661      */
19662     tickable : false,
19663     
19664     /**
19665      * Returns the element this view is bound to.
19666      * @return {Roo.Element}
19667      */
19668     getEl : function(){
19669         return this.wrapEl;
19670     },
19671     
19672     
19673
19674     /**
19675      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19676      */
19677     refresh : function(){
19678         //Roo.log('refresh');
19679         var t = this.tpl;
19680         
19681         // if we are using something like 'domtemplate', then
19682         // the what gets used is:
19683         // t.applySubtemplate(NAME, data, wrapping data..)
19684         // the outer template then get' applied with
19685         //     the store 'extra data'
19686         // and the body get's added to the
19687         //      roo-name="data" node?
19688         //      <span class='roo-tpl-{name}'></span> ?????
19689         
19690         
19691         
19692         this.clearSelections();
19693         this.el.update("");
19694         var html = [];
19695         var records = this.store.getRange();
19696         if(records.length < 1) {
19697             
19698             // is this valid??  = should it render a template??
19699             
19700             this.el.update(this.emptyText);
19701             return;
19702         }
19703         var el = this.el;
19704         if (this.dataName) {
19705             this.el.update(t.apply(this.store.meta)); //????
19706             el = this.el.child('.roo-tpl-' + this.dataName);
19707         }
19708         
19709         for(var i = 0, len = records.length; i < len; i++){
19710             var data = this.prepareData(records[i].data, i, records[i]);
19711             this.fireEvent("preparedata", this, data, i, records[i]);
19712             
19713             var d = Roo.apply({}, data);
19714             
19715             if(this.tickable){
19716                 Roo.apply(d, {'roo-id' : Roo.id()});
19717                 
19718                 var _this = this;
19719             
19720                 Roo.each(this.parent.item, function(item){
19721                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19722                         return;
19723                     }
19724                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19725                 });
19726             }
19727             
19728             html[html.length] = Roo.util.Format.trim(
19729                 this.dataName ?
19730                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19731                     t.apply(d)
19732             );
19733         }
19734         
19735         
19736         
19737         el.update(html.join(""));
19738         this.nodes = el.dom.childNodes;
19739         this.updateIndexes(0);
19740     },
19741     
19742
19743     /**
19744      * Function to override to reformat the data that is sent to
19745      * the template for each node.
19746      * DEPRICATED - use the preparedata event handler.
19747      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19748      * a JSON object for an UpdateManager bound view).
19749      */
19750     prepareData : function(data, index, record)
19751     {
19752         this.fireEvent("preparedata", this, data, index, record);
19753         return data;
19754     },
19755
19756     onUpdate : function(ds, record){
19757         // Roo.log('on update');   
19758         this.clearSelections();
19759         var index = this.store.indexOf(record);
19760         var n = this.nodes[index];
19761         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19762         n.parentNode.removeChild(n);
19763         this.updateIndexes(index, index);
19764     },
19765
19766     
19767     
19768 // --------- FIXME     
19769     onAdd : function(ds, records, index)
19770     {
19771         //Roo.log(['on Add', ds, records, index] );        
19772         this.clearSelections();
19773         if(this.nodes.length == 0){
19774             this.refresh();
19775             return;
19776         }
19777         var n = this.nodes[index];
19778         for(var i = 0, len = records.length; i < len; i++){
19779             var d = this.prepareData(records[i].data, i, records[i]);
19780             if(n){
19781                 this.tpl.insertBefore(n, d);
19782             }else{
19783                 
19784                 this.tpl.append(this.el, d);
19785             }
19786         }
19787         this.updateIndexes(index);
19788     },
19789
19790     onRemove : function(ds, record, index){
19791        // Roo.log('onRemove');
19792         this.clearSelections();
19793         var el = this.dataName  ?
19794             this.el.child('.roo-tpl-' + this.dataName) :
19795             this.el; 
19796         
19797         el.dom.removeChild(this.nodes[index]);
19798         this.updateIndexes(index);
19799     },
19800
19801     /**
19802      * Refresh an individual node.
19803      * @param {Number} index
19804      */
19805     refreshNode : function(index){
19806         this.onUpdate(this.store, this.store.getAt(index));
19807     },
19808
19809     updateIndexes : function(startIndex, endIndex){
19810         var ns = this.nodes;
19811         startIndex = startIndex || 0;
19812         endIndex = endIndex || ns.length - 1;
19813         for(var i = startIndex; i <= endIndex; i++){
19814             ns[i].nodeIndex = i;
19815         }
19816     },
19817
19818     /**
19819      * Changes the data store this view uses and refresh the view.
19820      * @param {Store} store
19821      */
19822     setStore : function(store, initial){
19823         if(!initial && this.store){
19824             this.store.un("datachanged", this.refresh);
19825             this.store.un("add", this.onAdd);
19826             this.store.un("remove", this.onRemove);
19827             this.store.un("update", this.onUpdate);
19828             this.store.un("clear", this.refresh);
19829             this.store.un("beforeload", this.onBeforeLoad);
19830             this.store.un("load", this.onLoad);
19831             this.store.un("loadexception", this.onLoad);
19832         }
19833         if(store){
19834           
19835             store.on("datachanged", this.refresh, this);
19836             store.on("add", this.onAdd, this);
19837             store.on("remove", this.onRemove, this);
19838             store.on("update", this.onUpdate, this);
19839             store.on("clear", this.refresh, this);
19840             store.on("beforeload", this.onBeforeLoad, this);
19841             store.on("load", this.onLoad, this);
19842             store.on("loadexception", this.onLoad, this);
19843         }
19844         
19845         if(store){
19846             this.refresh();
19847         }
19848     },
19849     /**
19850      * onbeforeLoad - masks the loading area.
19851      *
19852      */
19853     onBeforeLoad : function(store,opts)
19854     {
19855          //Roo.log('onBeforeLoad');   
19856         if (!opts.add) {
19857             this.el.update("");
19858         }
19859         this.el.mask(this.mask ? this.mask : "Loading" ); 
19860     },
19861     onLoad : function ()
19862     {
19863         this.el.unmask();
19864     },
19865     
19866
19867     /**
19868      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19869      * @param {HTMLElement} node
19870      * @return {HTMLElement} The template node
19871      */
19872     findItemFromChild : function(node){
19873         var el = this.dataName  ?
19874             this.el.child('.roo-tpl-' + this.dataName,true) :
19875             this.el.dom; 
19876         
19877         if(!node || node.parentNode == el){
19878                     return node;
19879             }
19880             var p = node.parentNode;
19881             while(p && p != el){
19882             if(p.parentNode == el){
19883                 return p;
19884             }
19885             p = p.parentNode;
19886         }
19887             return null;
19888     },
19889
19890     /** @ignore */
19891     onClick : function(e){
19892         var item = this.findItemFromChild(e.getTarget());
19893         if(item){
19894             var index = this.indexOf(item);
19895             if(this.onItemClick(item, index, e) !== false){
19896                 this.fireEvent("click", this, index, item, e);
19897             }
19898         }else{
19899             this.clearSelections();
19900         }
19901     },
19902
19903     /** @ignore */
19904     onContextMenu : function(e){
19905         var item = this.findItemFromChild(e.getTarget());
19906         if(item){
19907             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19908         }
19909     },
19910
19911     /** @ignore */
19912     onDblClick : function(e){
19913         var item = this.findItemFromChild(e.getTarget());
19914         if(item){
19915             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19916         }
19917     },
19918
19919     onItemClick : function(item, index, e)
19920     {
19921         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19922             return false;
19923         }
19924         if (this.toggleSelect) {
19925             var m = this.isSelected(item) ? 'unselect' : 'select';
19926             //Roo.log(m);
19927             var _t = this;
19928             _t[m](item, true, false);
19929             return true;
19930         }
19931         if(this.multiSelect || this.singleSelect){
19932             if(this.multiSelect && e.shiftKey && this.lastSelection){
19933                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19934             }else{
19935                 this.select(item, this.multiSelect && e.ctrlKey);
19936                 this.lastSelection = item;
19937             }
19938             
19939             if(!this.tickable){
19940                 e.preventDefault();
19941             }
19942             
19943         }
19944         return true;
19945     },
19946
19947     /**
19948      * Get the number of selected nodes.
19949      * @return {Number}
19950      */
19951     getSelectionCount : function(){
19952         return this.selections.length;
19953     },
19954
19955     /**
19956      * Get the currently selected nodes.
19957      * @return {Array} An array of HTMLElements
19958      */
19959     getSelectedNodes : function(){
19960         return this.selections;
19961     },
19962
19963     /**
19964      * Get the indexes of the selected nodes.
19965      * @return {Array}
19966      */
19967     getSelectedIndexes : function(){
19968         var indexes = [], s = this.selections;
19969         for(var i = 0, len = s.length; i < len; i++){
19970             indexes.push(s[i].nodeIndex);
19971         }
19972         return indexes;
19973     },
19974
19975     /**
19976      * Clear all selections
19977      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19978      */
19979     clearSelections : function(suppressEvent){
19980         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19981             this.cmp.elements = this.selections;
19982             this.cmp.removeClass(this.selectedClass);
19983             this.selections = [];
19984             if(!suppressEvent){
19985                 this.fireEvent("selectionchange", this, this.selections);
19986             }
19987         }
19988     },
19989
19990     /**
19991      * Returns true if the passed node is selected
19992      * @param {HTMLElement/Number} node The node or node index
19993      * @return {Boolean}
19994      */
19995     isSelected : function(node){
19996         var s = this.selections;
19997         if(s.length < 1){
19998             return false;
19999         }
20000         node = this.getNode(node);
20001         return s.indexOf(node) !== -1;
20002     },
20003
20004     /**
20005      * Selects nodes.
20006      * @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
20007      * @param {Boolean} keepExisting (optional) true to keep existing selections
20008      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20009      */
20010     select : function(nodeInfo, keepExisting, suppressEvent){
20011         if(nodeInfo instanceof Array){
20012             if(!keepExisting){
20013                 this.clearSelections(true);
20014             }
20015             for(var i = 0, len = nodeInfo.length; i < len; i++){
20016                 this.select(nodeInfo[i], true, true);
20017             }
20018             return;
20019         } 
20020         var node = this.getNode(nodeInfo);
20021         if(!node || this.isSelected(node)){
20022             return; // already selected.
20023         }
20024         if(!keepExisting){
20025             this.clearSelections(true);
20026         }
20027         
20028         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20029             Roo.fly(node).addClass(this.selectedClass);
20030             this.selections.push(node);
20031             if(!suppressEvent){
20032                 this.fireEvent("selectionchange", this, this.selections);
20033             }
20034         }
20035         
20036         
20037     },
20038       /**
20039      * Unselects nodes.
20040      * @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
20041      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20042      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20043      */
20044     unselect : function(nodeInfo, keepExisting, suppressEvent)
20045     {
20046         if(nodeInfo instanceof Array){
20047             Roo.each(this.selections, function(s) {
20048                 this.unselect(s, nodeInfo);
20049             }, this);
20050             return;
20051         }
20052         var node = this.getNode(nodeInfo);
20053         if(!node || !this.isSelected(node)){
20054             //Roo.log("not selected");
20055             return; // not selected.
20056         }
20057         // fireevent???
20058         var ns = [];
20059         Roo.each(this.selections, function(s) {
20060             if (s == node ) {
20061                 Roo.fly(node).removeClass(this.selectedClass);
20062
20063                 return;
20064             }
20065             ns.push(s);
20066         },this);
20067         
20068         this.selections= ns;
20069         this.fireEvent("selectionchange", this, this.selections);
20070     },
20071
20072     /**
20073      * Gets a template node.
20074      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20075      * @return {HTMLElement} The node or null if it wasn't found
20076      */
20077     getNode : function(nodeInfo){
20078         if(typeof nodeInfo == "string"){
20079             return document.getElementById(nodeInfo);
20080         }else if(typeof nodeInfo == "number"){
20081             return this.nodes[nodeInfo];
20082         }
20083         return nodeInfo;
20084     },
20085
20086     /**
20087      * Gets a range template nodes.
20088      * @param {Number} startIndex
20089      * @param {Number} endIndex
20090      * @return {Array} An array of nodes
20091      */
20092     getNodes : function(start, end){
20093         var ns = this.nodes;
20094         start = start || 0;
20095         end = typeof end == "undefined" ? ns.length - 1 : end;
20096         var nodes = [];
20097         if(start <= end){
20098             for(var i = start; i <= end; i++){
20099                 nodes.push(ns[i]);
20100             }
20101         } else{
20102             for(var i = start; i >= end; i--){
20103                 nodes.push(ns[i]);
20104             }
20105         }
20106         return nodes;
20107     },
20108
20109     /**
20110      * Finds the index of the passed node
20111      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20112      * @return {Number} The index of the node or -1
20113      */
20114     indexOf : function(node){
20115         node = this.getNode(node);
20116         if(typeof node.nodeIndex == "number"){
20117             return node.nodeIndex;
20118         }
20119         var ns = this.nodes;
20120         for(var i = 0, len = ns.length; i < len; i++){
20121             if(ns[i] == node){
20122                 return i;
20123             }
20124         }
20125         return -1;
20126     }
20127 });
20128 /*
20129  * - LGPL
20130  *
20131  * based on jquery fullcalendar
20132  * 
20133  */
20134
20135 Roo.bootstrap = Roo.bootstrap || {};
20136 /**
20137  * @class Roo.bootstrap.Calendar
20138  * @extends Roo.bootstrap.Component
20139  * Bootstrap Calendar class
20140  * @cfg {Boolean} loadMask (true|false) default false
20141  * @cfg {Object} header generate the user specific header of the calendar, default false
20142
20143  * @constructor
20144  * Create a new Container
20145  * @param {Object} config The config object
20146  */
20147
20148
20149
20150 Roo.bootstrap.Calendar = function(config){
20151     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20152      this.addEvents({
20153         /**
20154              * @event select
20155              * Fires when a date is selected
20156              * @param {DatePicker} this
20157              * @param {Date} date The selected date
20158              */
20159         'select': true,
20160         /**
20161              * @event monthchange
20162              * Fires when the displayed month changes 
20163              * @param {DatePicker} this
20164              * @param {Date} date The selected month
20165              */
20166         'monthchange': true,
20167         /**
20168              * @event evententer
20169              * Fires when mouse over an event
20170              * @param {Calendar} this
20171              * @param {event} Event
20172              */
20173         'evententer': true,
20174         /**
20175              * @event eventleave
20176              * Fires when the mouse leaves an
20177              * @param {Calendar} this
20178              * @param {event}
20179              */
20180         'eventleave': true,
20181         /**
20182              * @event eventclick
20183              * Fires when the mouse click an
20184              * @param {Calendar} this
20185              * @param {event}
20186              */
20187         'eventclick': true
20188         
20189     });
20190
20191 };
20192
20193 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20194     
20195      /**
20196      * @cfg {Number} startDay
20197      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20198      */
20199     startDay : 0,
20200     
20201     loadMask : false,
20202     
20203     header : false,
20204       
20205     getAutoCreate : function(){
20206         
20207         
20208         var fc_button = function(name, corner, style, content ) {
20209             return Roo.apply({},{
20210                 tag : 'span',
20211                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20212                          (corner.length ?
20213                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20214                             ''
20215                         ),
20216                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20217                 unselectable: 'on'
20218             });
20219         };
20220         
20221         var header = {};
20222         
20223         if(!this.header){
20224             header = {
20225                 tag : 'table',
20226                 cls : 'fc-header',
20227                 style : 'width:100%',
20228                 cn : [
20229                     {
20230                         tag: 'tr',
20231                         cn : [
20232                             {
20233                                 tag : 'td',
20234                                 cls : 'fc-header-left',
20235                                 cn : [
20236                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20237                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20238                                     { tag: 'span', cls: 'fc-header-space' },
20239                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20240
20241
20242                                 ]
20243                             },
20244
20245                             {
20246                                 tag : 'td',
20247                                 cls : 'fc-header-center',
20248                                 cn : [
20249                                     {
20250                                         tag: 'span',
20251                                         cls: 'fc-header-title',
20252                                         cn : {
20253                                             tag: 'H2',
20254                                             html : 'month / year'
20255                                         }
20256                                     }
20257
20258                                 ]
20259                             },
20260                             {
20261                                 tag : 'td',
20262                                 cls : 'fc-header-right',
20263                                 cn : [
20264                               /*      fc_button('month', 'left', '', 'month' ),
20265                                     fc_button('week', '', '', 'week' ),
20266                                     fc_button('day', 'right', '', 'day' )
20267                                 */    
20268
20269                                 ]
20270                             }
20271
20272                         ]
20273                     }
20274                 ]
20275             };
20276         }
20277         
20278         header = this.header;
20279         
20280        
20281         var cal_heads = function() {
20282             var ret = [];
20283             // fixme - handle this.
20284             
20285             for (var i =0; i < Date.dayNames.length; i++) {
20286                 var d = Date.dayNames[i];
20287                 ret.push({
20288                     tag: 'th',
20289                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20290                     html : d.substring(0,3)
20291                 });
20292                 
20293             }
20294             ret[0].cls += ' fc-first';
20295             ret[6].cls += ' fc-last';
20296             return ret;
20297         };
20298         var cal_cell = function(n) {
20299             return  {
20300                 tag: 'td',
20301                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20302                 cn : [
20303                     {
20304                         cn : [
20305                             {
20306                                 cls: 'fc-day-number',
20307                                 html: 'D'
20308                             },
20309                             {
20310                                 cls: 'fc-day-content',
20311                              
20312                                 cn : [
20313                                      {
20314                                         style: 'position: relative;' // height: 17px;
20315                                     }
20316                                 ]
20317                             }
20318                             
20319                             
20320                         ]
20321                     }
20322                 ]
20323                 
20324             }
20325         };
20326         var cal_rows = function() {
20327             
20328             var ret = [];
20329             for (var r = 0; r < 6; r++) {
20330                 var row= {
20331                     tag : 'tr',
20332                     cls : 'fc-week',
20333                     cn : []
20334                 };
20335                 
20336                 for (var i =0; i < Date.dayNames.length; i++) {
20337                     var d = Date.dayNames[i];
20338                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20339
20340                 }
20341                 row.cn[0].cls+=' fc-first';
20342                 row.cn[0].cn[0].style = 'min-height:90px';
20343                 row.cn[6].cls+=' fc-last';
20344                 ret.push(row);
20345                 
20346             }
20347             ret[0].cls += ' fc-first';
20348             ret[4].cls += ' fc-prev-last';
20349             ret[5].cls += ' fc-last';
20350             return ret;
20351             
20352         };
20353         
20354         var cal_table = {
20355             tag: 'table',
20356             cls: 'fc-border-separate',
20357             style : 'width:100%',
20358             cellspacing  : 0,
20359             cn : [
20360                 { 
20361                     tag: 'thead',
20362                     cn : [
20363                         { 
20364                             tag: 'tr',
20365                             cls : 'fc-first fc-last',
20366                             cn : cal_heads()
20367                         }
20368                     ]
20369                 },
20370                 { 
20371                     tag: 'tbody',
20372                     cn : cal_rows()
20373                 }
20374                   
20375             ]
20376         };
20377          
20378          var cfg = {
20379             cls : 'fc fc-ltr',
20380             cn : [
20381                 header,
20382                 {
20383                     cls : 'fc-content',
20384                     style : "position: relative;",
20385                     cn : [
20386                         {
20387                             cls : 'fc-view fc-view-month fc-grid',
20388                             style : 'position: relative',
20389                             unselectable : 'on',
20390                             cn : [
20391                                 {
20392                                     cls : 'fc-event-container',
20393                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20394                                 },
20395                                 cal_table
20396                             ]
20397                         }
20398                     ]
20399     
20400                 }
20401            ] 
20402             
20403         };
20404         
20405          
20406         
20407         return cfg;
20408     },
20409     
20410     
20411     initEvents : function()
20412     {
20413         if(!this.store){
20414             throw "can not find store for calendar";
20415         }
20416         
20417         var mark = {
20418             tag: "div",
20419             cls:"x-dlg-mask",
20420             style: "text-align:center",
20421             cn: [
20422                 {
20423                     tag: "div",
20424                     style: "background-color:white;width:50%;margin:250 auto",
20425                     cn: [
20426                         {
20427                             tag: "img",
20428                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20429                         },
20430                         {
20431                             tag: "span",
20432                             html: "Loading"
20433                         }
20434                         
20435                     ]
20436                 }
20437             ]
20438         };
20439         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20440         
20441         var size = this.el.select('.fc-content', true).first().getSize();
20442         this.maskEl.setSize(size.width, size.height);
20443         this.maskEl.enableDisplayMode("block");
20444         if(!this.loadMask){
20445             this.maskEl.hide();
20446         }
20447         
20448         this.store = Roo.factory(this.store, Roo.data);
20449         this.store.on('load', this.onLoad, this);
20450         this.store.on('beforeload', this.onBeforeLoad, this);
20451         
20452         this.resize();
20453         
20454         this.cells = this.el.select('.fc-day',true);
20455         //Roo.log(this.cells);
20456         this.textNodes = this.el.query('.fc-day-number');
20457         this.cells.addClassOnOver('fc-state-hover');
20458         
20459         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20460         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20461         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20462         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20463         
20464         this.on('monthchange', this.onMonthChange, this);
20465         
20466         this.update(new Date().clearTime());
20467     },
20468     
20469     resize : function() {
20470         var sz  = this.el.getSize();
20471         
20472         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20473         this.el.select('.fc-day-content div',true).setHeight(34);
20474     },
20475     
20476     
20477     // private
20478     showPrevMonth : function(e){
20479         this.update(this.activeDate.add("mo", -1));
20480     },
20481     showToday : function(e){
20482         this.update(new Date().clearTime());
20483     },
20484     // private
20485     showNextMonth : function(e){
20486         this.update(this.activeDate.add("mo", 1));
20487     },
20488
20489     // private
20490     showPrevYear : function(){
20491         this.update(this.activeDate.add("y", -1));
20492     },
20493
20494     // private
20495     showNextYear : function(){
20496         this.update(this.activeDate.add("y", 1));
20497     },
20498
20499     
20500    // private
20501     update : function(date)
20502     {
20503         var vd = this.activeDate;
20504         this.activeDate = date;
20505 //        if(vd && this.el){
20506 //            var t = date.getTime();
20507 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20508 //                Roo.log('using add remove');
20509 //                
20510 //                this.fireEvent('monthchange', this, date);
20511 //                
20512 //                this.cells.removeClass("fc-state-highlight");
20513 //                this.cells.each(function(c){
20514 //                   if(c.dateValue == t){
20515 //                       c.addClass("fc-state-highlight");
20516 //                       setTimeout(function(){
20517 //                            try{c.dom.firstChild.focus();}catch(e){}
20518 //                       }, 50);
20519 //                       return false;
20520 //                   }
20521 //                   return true;
20522 //                });
20523 //                return;
20524 //            }
20525 //        }
20526         
20527         var days = date.getDaysInMonth();
20528         
20529         var firstOfMonth = date.getFirstDateOfMonth();
20530         var startingPos = firstOfMonth.getDay()-this.startDay;
20531         
20532         if(startingPos < this.startDay){
20533             startingPos += 7;
20534         }
20535         
20536         var pm = date.add(Date.MONTH, -1);
20537         var prevStart = pm.getDaysInMonth()-startingPos;
20538 //        
20539         this.cells = this.el.select('.fc-day',true);
20540         this.textNodes = this.el.query('.fc-day-number');
20541         this.cells.addClassOnOver('fc-state-hover');
20542         
20543         var cells = this.cells.elements;
20544         var textEls = this.textNodes;
20545         
20546         Roo.each(cells, function(cell){
20547             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20548         });
20549         
20550         days += startingPos;
20551
20552         // convert everything to numbers so it's fast
20553         var day = 86400000;
20554         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20555         //Roo.log(d);
20556         //Roo.log(pm);
20557         //Roo.log(prevStart);
20558         
20559         var today = new Date().clearTime().getTime();
20560         var sel = date.clearTime().getTime();
20561         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20562         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20563         var ddMatch = this.disabledDatesRE;
20564         var ddText = this.disabledDatesText;
20565         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20566         var ddaysText = this.disabledDaysText;
20567         var format = this.format;
20568         
20569         var setCellClass = function(cal, cell){
20570             cell.row = 0;
20571             cell.events = [];
20572             cell.more = [];
20573             //Roo.log('set Cell Class');
20574             cell.title = "";
20575             var t = d.getTime();
20576             
20577             //Roo.log(d);
20578             
20579             cell.dateValue = t;
20580             if(t == today){
20581                 cell.className += " fc-today";
20582                 cell.className += " fc-state-highlight";
20583                 cell.title = cal.todayText;
20584             }
20585             if(t == sel){
20586                 // disable highlight in other month..
20587                 //cell.className += " fc-state-highlight";
20588                 
20589             }
20590             // disabling
20591             if(t < min) {
20592                 cell.className = " fc-state-disabled";
20593                 cell.title = cal.minText;
20594                 return;
20595             }
20596             if(t > max) {
20597                 cell.className = " fc-state-disabled";
20598                 cell.title = cal.maxText;
20599                 return;
20600             }
20601             if(ddays){
20602                 if(ddays.indexOf(d.getDay()) != -1){
20603                     cell.title = ddaysText;
20604                     cell.className = " fc-state-disabled";
20605                 }
20606             }
20607             if(ddMatch && format){
20608                 var fvalue = d.dateFormat(format);
20609                 if(ddMatch.test(fvalue)){
20610                     cell.title = ddText.replace("%0", fvalue);
20611                     cell.className = " fc-state-disabled";
20612                 }
20613             }
20614             
20615             if (!cell.initialClassName) {
20616                 cell.initialClassName = cell.dom.className;
20617             }
20618             
20619             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20620         };
20621
20622         var i = 0;
20623         
20624         for(; i < startingPos; i++) {
20625             textEls[i].innerHTML = (++prevStart);
20626             d.setDate(d.getDate()+1);
20627             
20628             cells[i].className = "fc-past fc-other-month";
20629             setCellClass(this, cells[i]);
20630         }
20631         
20632         var intDay = 0;
20633         
20634         for(; i < days; i++){
20635             intDay = i - startingPos + 1;
20636             textEls[i].innerHTML = (intDay);
20637             d.setDate(d.getDate()+1);
20638             
20639             cells[i].className = ''; // "x-date-active";
20640             setCellClass(this, cells[i]);
20641         }
20642         var extraDays = 0;
20643         
20644         for(; i < 42; i++) {
20645             textEls[i].innerHTML = (++extraDays);
20646             d.setDate(d.getDate()+1);
20647             
20648             cells[i].className = "fc-future fc-other-month";
20649             setCellClass(this, cells[i]);
20650         }
20651         
20652         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20653         
20654         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20655         
20656         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20657         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20658         
20659         if(totalRows != 6){
20660             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20661             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20662         }
20663         
20664         this.fireEvent('monthchange', this, date);
20665         
20666         
20667         /*
20668         if(!this.internalRender){
20669             var main = this.el.dom.firstChild;
20670             var w = main.offsetWidth;
20671             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20672             Roo.fly(main).setWidth(w);
20673             this.internalRender = true;
20674             // opera does not respect the auto grow header center column
20675             // then, after it gets a width opera refuses to recalculate
20676             // without a second pass
20677             if(Roo.isOpera && !this.secondPass){
20678                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20679                 this.secondPass = true;
20680                 this.update.defer(10, this, [date]);
20681             }
20682         }
20683         */
20684         
20685     },
20686     
20687     findCell : function(dt) {
20688         dt = dt.clearTime().getTime();
20689         var ret = false;
20690         this.cells.each(function(c){
20691             //Roo.log("check " +c.dateValue + '?=' + dt);
20692             if(c.dateValue == dt){
20693                 ret = c;
20694                 return false;
20695             }
20696             return true;
20697         });
20698         
20699         return ret;
20700     },
20701     
20702     findCells : function(ev) {
20703         var s = ev.start.clone().clearTime().getTime();
20704        // Roo.log(s);
20705         var e= ev.end.clone().clearTime().getTime();
20706        // Roo.log(e);
20707         var ret = [];
20708         this.cells.each(function(c){
20709              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20710             
20711             if(c.dateValue > e){
20712                 return ;
20713             }
20714             if(c.dateValue < s){
20715                 return ;
20716             }
20717             ret.push(c);
20718         });
20719         
20720         return ret;    
20721     },
20722     
20723 //    findBestRow: function(cells)
20724 //    {
20725 //        var ret = 0;
20726 //        
20727 //        for (var i =0 ; i < cells.length;i++) {
20728 //            ret  = Math.max(cells[i].rows || 0,ret);
20729 //        }
20730 //        return ret;
20731 //        
20732 //    },
20733     
20734     
20735     addItem : function(ev)
20736     {
20737         // look for vertical location slot in
20738         var cells = this.findCells(ev);
20739         
20740 //        ev.row = this.findBestRow(cells);
20741         
20742         // work out the location.
20743         
20744         var crow = false;
20745         var rows = [];
20746         for(var i =0; i < cells.length; i++) {
20747             
20748             cells[i].row = cells[0].row;
20749             
20750             if(i == 0){
20751                 cells[i].row = cells[i].row + 1;
20752             }
20753             
20754             if (!crow) {
20755                 crow = {
20756                     start : cells[i],
20757                     end :  cells[i]
20758                 };
20759                 continue;
20760             }
20761             if (crow.start.getY() == cells[i].getY()) {
20762                 // on same row.
20763                 crow.end = cells[i];
20764                 continue;
20765             }
20766             // different row.
20767             rows.push(crow);
20768             crow = {
20769                 start: cells[i],
20770                 end : cells[i]
20771             };
20772             
20773         }
20774         
20775         rows.push(crow);
20776         ev.els = [];
20777         ev.rows = rows;
20778         ev.cells = cells;
20779         
20780         cells[0].events.push(ev);
20781         
20782         this.calevents.push(ev);
20783     },
20784     
20785     clearEvents: function() {
20786         
20787         if(!this.calevents){
20788             return;
20789         }
20790         
20791         Roo.each(this.cells.elements, function(c){
20792             c.row = 0;
20793             c.events = [];
20794             c.more = [];
20795         });
20796         
20797         Roo.each(this.calevents, function(e) {
20798             Roo.each(e.els, function(el) {
20799                 el.un('mouseenter' ,this.onEventEnter, this);
20800                 el.un('mouseleave' ,this.onEventLeave, this);
20801                 el.remove();
20802             },this);
20803         },this);
20804         
20805         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20806             e.remove();
20807         });
20808         
20809     },
20810     
20811     renderEvents: function()
20812     {   
20813         var _this = this;
20814         
20815         this.cells.each(function(c) {
20816             
20817             if(c.row < 5){
20818                 return;
20819             }
20820             
20821             var ev = c.events;
20822             
20823             var r = 4;
20824             if(c.row != c.events.length){
20825                 r = 4 - (4 - (c.row - c.events.length));
20826             }
20827             
20828             c.events = ev.slice(0, r);
20829             c.more = ev.slice(r);
20830             
20831             if(c.more.length && c.more.length == 1){
20832                 c.events.push(c.more.pop());
20833             }
20834             
20835             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20836             
20837         });
20838             
20839         this.cells.each(function(c) {
20840             
20841             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20842             
20843             
20844             for (var e = 0; e < c.events.length; e++){
20845                 var ev = c.events[e];
20846                 var rows = ev.rows;
20847                 
20848                 for(var i = 0; i < rows.length; i++) {
20849                 
20850                     // how many rows should it span..
20851
20852                     var  cfg = {
20853                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20854                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20855
20856                         unselectable : "on",
20857                         cn : [
20858                             {
20859                                 cls: 'fc-event-inner',
20860                                 cn : [
20861     //                                {
20862     //                                  tag:'span',
20863     //                                  cls: 'fc-event-time',
20864     //                                  html : cells.length > 1 ? '' : ev.time
20865     //                                },
20866                                     {
20867                                       tag:'span',
20868                                       cls: 'fc-event-title',
20869                                       html : String.format('{0}', ev.title)
20870                                     }
20871
20872
20873                                 ]
20874                             },
20875                             {
20876                                 cls: 'ui-resizable-handle ui-resizable-e',
20877                                 html : '&nbsp;&nbsp;&nbsp'
20878                             }
20879
20880                         ]
20881                     };
20882
20883                     if (i == 0) {
20884                         cfg.cls += ' fc-event-start';
20885                     }
20886                     if ((i+1) == rows.length) {
20887                         cfg.cls += ' fc-event-end';
20888                     }
20889
20890                     var ctr = _this.el.select('.fc-event-container',true).first();
20891                     var cg = ctr.createChild(cfg);
20892
20893                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20894                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20895
20896                     var r = (c.more.length) ? 1 : 0;
20897                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20898                     cg.setWidth(ebox.right - sbox.x -2);
20899
20900                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20901                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20902                     cg.on('click', _this.onEventClick, _this, ev);
20903
20904                     ev.els.push(cg);
20905                     
20906                 }
20907                 
20908             }
20909             
20910             
20911             if(c.more.length){
20912                 var  cfg = {
20913                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20914                     style : 'position: absolute',
20915                     unselectable : "on",
20916                     cn : [
20917                         {
20918                             cls: 'fc-event-inner',
20919                             cn : [
20920                                 {
20921                                   tag:'span',
20922                                   cls: 'fc-event-title',
20923                                   html : 'More'
20924                                 }
20925
20926
20927                             ]
20928                         },
20929                         {
20930                             cls: 'ui-resizable-handle ui-resizable-e',
20931                             html : '&nbsp;&nbsp;&nbsp'
20932                         }
20933
20934                     ]
20935                 };
20936
20937                 var ctr = _this.el.select('.fc-event-container',true).first();
20938                 var cg = ctr.createChild(cfg);
20939
20940                 var sbox = c.select('.fc-day-content',true).first().getBox();
20941                 var ebox = c.select('.fc-day-content',true).first().getBox();
20942                 //Roo.log(cg);
20943                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20944                 cg.setWidth(ebox.right - sbox.x -2);
20945
20946                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20947                 
20948             }
20949             
20950         });
20951         
20952         
20953         
20954     },
20955     
20956     onEventEnter: function (e, el,event,d) {
20957         this.fireEvent('evententer', this, el, event);
20958     },
20959     
20960     onEventLeave: function (e, el,event,d) {
20961         this.fireEvent('eventleave', this, el, event);
20962     },
20963     
20964     onEventClick: function (e, el,event,d) {
20965         this.fireEvent('eventclick', this, el, event);
20966     },
20967     
20968     onMonthChange: function () {
20969         this.store.load();
20970     },
20971     
20972     onMoreEventClick: function(e, el, more)
20973     {
20974         var _this = this;
20975         
20976         this.calpopover.placement = 'right';
20977         this.calpopover.setTitle('More');
20978         
20979         this.calpopover.setContent('');
20980         
20981         var ctr = this.calpopover.el.select('.popover-content', true).first();
20982         
20983         Roo.each(more, function(m){
20984             var cfg = {
20985                 cls : 'fc-event-hori fc-event-draggable',
20986                 html : m.title
20987             };
20988             var cg = ctr.createChild(cfg);
20989             
20990             cg.on('click', _this.onEventClick, _this, m);
20991         });
20992         
20993         this.calpopover.show(el);
20994         
20995         
20996     },
20997     
20998     onLoad: function () 
20999     {   
21000         this.calevents = [];
21001         var cal = this;
21002         
21003         if(this.store.getCount() > 0){
21004             this.store.data.each(function(d){
21005                cal.addItem({
21006                     id : d.data.id,
21007                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21008                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21009                     time : d.data.start_time,
21010                     title : d.data.title,
21011                     description : d.data.description,
21012                     venue : d.data.venue
21013                 });
21014             });
21015         }
21016         
21017         this.renderEvents();
21018         
21019         if(this.calevents.length && this.loadMask){
21020             this.maskEl.hide();
21021         }
21022     },
21023     
21024     onBeforeLoad: function()
21025     {
21026         this.clearEvents();
21027         if(this.loadMask){
21028             this.maskEl.show();
21029         }
21030     }
21031 });
21032
21033  
21034  /*
21035  * - LGPL
21036  *
21037  * element
21038  * 
21039  */
21040
21041 /**
21042  * @class Roo.bootstrap.Popover
21043  * @extends Roo.bootstrap.Component
21044  * Bootstrap Popover class
21045  * @cfg {String} html contents of the popover   (or false to use children..)
21046  * @cfg {String} title of popover (or false to hide)
21047  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21048  * @cfg {String} trigger click || hover (or false to trigger manually)
21049  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21050  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21051  *      - if false and it has a 'parent' then it will be automatically added to that element
21052  *      - if string - Roo.get  will be called 
21053  * @cfg {Number} delay - delay before showing
21054  
21055  * @constructor
21056  * Create a new Popover
21057  * @param {Object} config The config object
21058  */
21059
21060 Roo.bootstrap.Popover = function(config){
21061     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21062     
21063     this.addEvents({
21064         // raw events
21065          /**
21066          * @event show
21067          * After the popover show
21068          * 
21069          * @param {Roo.bootstrap.Popover} this
21070          */
21071         "show" : true,
21072         /**
21073          * @event hide
21074          * After the popover hide
21075          * 
21076          * @param {Roo.bootstrap.Popover} this
21077          */
21078         "hide" : true
21079     });
21080 };
21081
21082 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21083     
21084     title: false,
21085     html: false,
21086     
21087     placement : 'right',
21088     trigger : 'hover', // hover
21089     modal : false,
21090     delay : 0,
21091     
21092     over: false,
21093     
21094     can_build_overlaid : false,
21095     
21096     maskEl : false, // the mask element
21097     headerEl : false,
21098     contentEl : false,
21099     alignEl : false, // when show is called with an element - this get's stored.
21100     
21101     getChildContainer : function()
21102     {
21103         return this.contentEl;
21104         
21105     },
21106     getPopoverHeader : function()
21107     {
21108         this.title = true; // flag not to hide it..
21109         this.headerEl.addClass('p-0');
21110         return this.headerEl
21111     },
21112     
21113     
21114     getAutoCreate : function(){
21115          
21116         var cfg = {
21117            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21118            style: 'display:block',
21119            cn : [
21120                 {
21121                     cls : 'arrow'
21122                 },
21123                 {
21124                     cls : 'popover-inner ',
21125                     cn : [
21126                         {
21127                             tag: 'h3',
21128                             cls: 'popover-title popover-header',
21129                             html : this.title === false ? '' : this.title
21130                         },
21131                         {
21132                             cls : 'popover-content popover-body '  + (this.cls || ''),
21133                             html : this.html || ''
21134                         }
21135                     ]
21136                     
21137                 }
21138            ]
21139         };
21140         
21141         return cfg;
21142     },
21143     /**
21144      * @param {string} the title
21145      */
21146     setTitle: function(str)
21147     {
21148         this.title = str;
21149         if (this.el) {
21150             this.headerEl.dom.innerHTML = str;
21151         }
21152         
21153     },
21154     /**
21155      * @param {string} the body content
21156      */
21157     setContent: function(str)
21158     {
21159         this.html = str;
21160         if (this.contentEl) {
21161             this.contentEl.dom.innerHTML = str;
21162         }
21163         
21164     },
21165     // as it get's added to the bottom of the page.
21166     onRender : function(ct, position)
21167     {
21168         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21169         
21170         
21171         
21172         if(!this.el){
21173             var cfg = Roo.apply({},  this.getAutoCreate());
21174             cfg.id = Roo.id();
21175             
21176             if (this.cls) {
21177                 cfg.cls += ' ' + this.cls;
21178             }
21179             if (this.style) {
21180                 cfg.style = this.style;
21181             }
21182             //Roo.log("adding to ");
21183             this.el = Roo.get(document.body).createChild(cfg, position);
21184 //            Roo.log(this.el);
21185         }
21186         
21187         this.contentEl = this.el.select('.popover-content',true).first();
21188         this.headerEl =  this.el.select('.popover-title',true).first();
21189         
21190         var nitems = [];
21191         if(typeof(this.items) != 'undefined'){
21192             var items = this.items;
21193             delete this.items;
21194
21195             for(var i =0;i < items.length;i++) {
21196                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21197             }
21198         }
21199
21200         this.items = nitems;
21201         
21202         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21203         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21204         
21205         
21206         
21207         this.initEvents();
21208     },
21209     
21210     resizeMask : function()
21211     {
21212         this.maskEl.setSize(
21213             Roo.lib.Dom.getViewWidth(true),
21214             Roo.lib.Dom.getViewHeight(true)
21215         );
21216     },
21217     
21218     initEvents : function()
21219     {
21220         
21221         if (!this.modal) { 
21222             Roo.bootstrap.Popover.register(this);
21223         }
21224          
21225         this.arrowEl = this.el.select('.arrow',true).first();
21226         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21227         this.el.enableDisplayMode('block');
21228         this.el.hide();
21229  
21230         
21231         if (this.over === false && !this.parent()) {
21232             return; 
21233         }
21234         if (this.triggers === false) {
21235             return;
21236         }
21237          
21238         // support parent
21239         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21240         var triggers = this.trigger ? this.trigger.split(' ') : [];
21241         Roo.each(triggers, function(trigger) {
21242         
21243             if (trigger == 'click') {
21244                 on_el.on('click', this.toggle, this);
21245             } else if (trigger != 'manual') {
21246                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21247                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21248       
21249                 on_el.on(eventIn  ,this.enter, this);
21250                 on_el.on(eventOut, this.leave, this);
21251             }
21252         }, this);
21253     },
21254     
21255     
21256     // private
21257     timeout : null,
21258     hoverState : null,
21259     
21260     toggle : function () {
21261         this.hoverState == 'in' ? this.leave() : this.enter();
21262     },
21263     
21264     enter : function () {
21265         
21266         clearTimeout(this.timeout);
21267     
21268         this.hoverState = 'in';
21269     
21270         if (!this.delay || !this.delay.show) {
21271             this.show();
21272             return;
21273         }
21274         var _t = this;
21275         this.timeout = setTimeout(function () {
21276             if (_t.hoverState == 'in') {
21277                 _t.show();
21278             }
21279         }, this.delay.show)
21280     },
21281     
21282     leave : function() {
21283         clearTimeout(this.timeout);
21284     
21285         this.hoverState = 'out';
21286     
21287         if (!this.delay || !this.delay.hide) {
21288             this.hide();
21289             return;
21290         }
21291         var _t = this;
21292         this.timeout = setTimeout(function () {
21293             if (_t.hoverState == 'out') {
21294                 _t.hide();
21295             }
21296         }, this.delay.hide)
21297     },
21298     /**
21299      * Show the popover
21300      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21301      * @param {string} (left|right|top|bottom) position
21302      */
21303     show : function (on_el, placement)
21304     {
21305         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21306         on_el = on_el || false; // default to false
21307          
21308         if (!on_el) {
21309             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21310                 on_el = this.parent().el;
21311             } else if (this.over) {
21312                 on_el = Roo.get(this.over);
21313             }
21314             
21315         }
21316         
21317         this.alignEl = Roo.get( on_el );
21318
21319         if (!this.el) {
21320             this.render(document.body);
21321         }
21322         
21323         
21324          
21325         
21326         if (this.title === false) {
21327             this.headerEl.hide();
21328         }
21329         
21330        
21331         this.el.show();
21332         this.el.dom.style.display = 'block';
21333          
21334  
21335         if (this.alignEl) {
21336             this.updatePosition(this.placement, true);
21337              
21338         } else {
21339             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21340             var es = this.el.getSize();
21341             var x = Roo.lib.Dom.getViewWidth()/2;
21342             var y = Roo.lib.Dom.getViewHeight()/2;
21343             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21344             
21345         }
21346
21347         
21348         //var arrow = this.el.select('.arrow',true).first();
21349         //arrow.set(align[2], 
21350         
21351         this.el.addClass('in');
21352         
21353          
21354         
21355         this.hoverState = 'in';
21356         
21357         if (this.modal) {
21358             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21359             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21360             this.maskEl.dom.style.display = 'block';
21361             this.maskEl.addClass('show');
21362         }
21363         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21364  
21365         this.fireEvent('show', this);
21366         
21367     },
21368     /**
21369      * fire this manually after loading a grid in the table for example
21370      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21371      * @param {Boolean} try and move it if we cant get right position.
21372      */
21373     updatePosition : function(placement, try_move)
21374     {
21375         // allow for calling with no parameters
21376         placement = placement   ? placement :  this.placement;
21377         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21378         
21379         this.el.removeClass([
21380             'fade','top','bottom', 'left', 'right','in',
21381             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21382         ]);
21383         this.el.addClass(placement + ' bs-popover-' + placement);
21384         
21385         if (!this.alignEl ) {
21386             return false;
21387         }
21388         
21389         switch (placement) {
21390             case 'right':
21391                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21392                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21393                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21394                     //normal display... or moved up/down.
21395                     this.el.setXY(offset);
21396                     var xy = this.alignEl.getAnchorXY('tr', false);
21397                     xy[0]+=2;xy[1]+=5;
21398                     this.arrowEl.setXY(xy);
21399                     return true;
21400                 }
21401                 // continue through...
21402                 return this.updatePosition('left', false);
21403                 
21404             
21405             case 'left':
21406                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21407                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21408                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21409                     //normal display... or moved up/down.
21410                     this.el.setXY(offset);
21411                     var xy = this.alignEl.getAnchorXY('tl', false);
21412                     xy[0]-=10;xy[1]+=5; // << fix me
21413                     this.arrowEl.setXY(xy);
21414                     return true;
21415                 }
21416                 // call self...
21417                 return this.updatePosition('right', false);
21418             
21419             case 'top':
21420                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21421                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21422                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21423                     //normal display... or moved up/down.
21424                     this.el.setXY(offset);
21425                     var xy = this.alignEl.getAnchorXY('t', false);
21426                     xy[1]-=10; // << fix me
21427                     this.arrowEl.setXY(xy);
21428                     return true;
21429                 }
21430                 // fall through
21431                return this.updatePosition('bottom', false);
21432             
21433             case 'bottom':
21434                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21435                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21436                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21437                     //normal display... or moved up/down.
21438                     this.el.setXY(offset);
21439                     var xy = this.alignEl.getAnchorXY('b', false);
21440                      xy[1]+=2; // << fix me
21441                     this.arrowEl.setXY(xy);
21442                     return true;
21443                 }
21444                 // fall through
21445                 return this.updatePosition('top', false);
21446                 
21447             
21448         }
21449         
21450         
21451         return false;
21452     },
21453     
21454     hide : function()
21455     {
21456         this.el.setXY([0,0]);
21457         this.el.removeClass('in');
21458         this.el.hide();
21459         this.hoverState = null;
21460         this.maskEl.hide(); // always..
21461         this.fireEvent('hide', this);
21462     }
21463     
21464 });
21465
21466
21467 Roo.apply(Roo.bootstrap.Popover, {
21468
21469     alignment : {
21470         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21471         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21472         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21473         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21474     },
21475     
21476     zIndex : 20001,
21477
21478     clickHander : false,
21479     
21480     
21481
21482     onMouseDown : function(e)
21483     {
21484         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21485             /// what is nothing is showing..
21486             this.hideAll();
21487         }
21488          
21489     },
21490     
21491     
21492     popups : [],
21493     
21494     register : function(popup)
21495     {
21496         if (!Roo.bootstrap.Popover.clickHandler) {
21497             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21498         }
21499         // hide other popups.
21500         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21501         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21502         this.hideAll(); //<< why?
21503         //this.popups.push(popup);
21504     },
21505     hideAll : function()
21506     {
21507         this.popups.forEach(function(p) {
21508             p.hide();
21509         });
21510     },
21511     onShow : function() {
21512         Roo.bootstrap.Popover.popups.push(this);
21513     },
21514     onHide : function() {
21515         Roo.bootstrap.Popover.popups.remove(this);
21516     } 
21517
21518 });/*
21519  * - LGPL
21520  *
21521  * Card header - holder for the card header elements.
21522  * 
21523  */
21524
21525 /**
21526  * @class Roo.bootstrap.PopoverNav
21527  * @extends Roo.bootstrap.NavGroup
21528  * Bootstrap Popover header navigation class
21529  * @constructor
21530  * Create a new Popover Header Navigation 
21531  * @param {Object} config The config object
21532  */
21533
21534 Roo.bootstrap.PopoverNav = function(config){
21535     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21536 };
21537
21538 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21539     
21540     
21541     container_method : 'getPopoverHeader' 
21542     
21543      
21544     
21545     
21546    
21547 });
21548
21549  
21550
21551  /*
21552  * - LGPL
21553  *
21554  * Progress
21555  * 
21556  */
21557
21558 /**
21559  * @class Roo.bootstrap.Progress
21560  * @extends Roo.bootstrap.Component
21561  * Bootstrap Progress class
21562  * @cfg {Boolean} striped striped of the progress bar
21563  * @cfg {Boolean} active animated of the progress bar
21564  * 
21565  * 
21566  * @constructor
21567  * Create a new Progress
21568  * @param {Object} config The config object
21569  */
21570
21571 Roo.bootstrap.Progress = function(config){
21572     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21573 };
21574
21575 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21576     
21577     striped : false,
21578     active: false,
21579     
21580     getAutoCreate : function(){
21581         var cfg = {
21582             tag: 'div',
21583             cls: 'progress'
21584         };
21585         
21586         
21587         if(this.striped){
21588             cfg.cls += ' progress-striped';
21589         }
21590       
21591         if(this.active){
21592             cfg.cls += ' active';
21593         }
21594         
21595         
21596         return cfg;
21597     }
21598    
21599 });
21600
21601  
21602
21603  /*
21604  * - LGPL
21605  *
21606  * ProgressBar
21607  * 
21608  */
21609
21610 /**
21611  * @class Roo.bootstrap.ProgressBar
21612  * @extends Roo.bootstrap.Component
21613  * Bootstrap ProgressBar class
21614  * @cfg {Number} aria_valuenow aria-value now
21615  * @cfg {Number} aria_valuemin aria-value min
21616  * @cfg {Number} aria_valuemax aria-value max
21617  * @cfg {String} label label for the progress bar
21618  * @cfg {String} panel (success | info | warning | danger )
21619  * @cfg {String} role role of the progress bar
21620  * @cfg {String} sr_only text
21621  * 
21622  * 
21623  * @constructor
21624  * Create a new ProgressBar
21625  * @param {Object} config The config object
21626  */
21627
21628 Roo.bootstrap.ProgressBar = function(config){
21629     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21630 };
21631
21632 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21633     
21634     aria_valuenow : 0,
21635     aria_valuemin : 0,
21636     aria_valuemax : 100,
21637     label : false,
21638     panel : false,
21639     role : false,
21640     sr_only: false,
21641     
21642     getAutoCreate : function()
21643     {
21644         
21645         var cfg = {
21646             tag: 'div',
21647             cls: 'progress-bar',
21648             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21649         };
21650         
21651         if(this.sr_only){
21652             cfg.cn = {
21653                 tag: 'span',
21654                 cls: 'sr-only',
21655                 html: this.sr_only
21656             }
21657         }
21658         
21659         if(this.role){
21660             cfg.role = this.role;
21661         }
21662         
21663         if(this.aria_valuenow){
21664             cfg['aria-valuenow'] = this.aria_valuenow;
21665         }
21666         
21667         if(this.aria_valuemin){
21668             cfg['aria-valuemin'] = this.aria_valuemin;
21669         }
21670         
21671         if(this.aria_valuemax){
21672             cfg['aria-valuemax'] = this.aria_valuemax;
21673         }
21674         
21675         if(this.label && !this.sr_only){
21676             cfg.html = this.label;
21677         }
21678         
21679         if(this.panel){
21680             cfg.cls += ' progress-bar-' + this.panel;
21681         }
21682         
21683         return cfg;
21684     },
21685     
21686     update : function(aria_valuenow)
21687     {
21688         this.aria_valuenow = aria_valuenow;
21689         
21690         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21691     }
21692    
21693 });
21694
21695  
21696
21697  /*
21698  * - LGPL
21699  *
21700  * column
21701  * 
21702  */
21703
21704 /**
21705  * @class Roo.bootstrap.TabGroup
21706  * @extends Roo.bootstrap.Column
21707  * Bootstrap Column class
21708  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21709  * @cfg {Boolean} carousel true to make the group behave like a carousel
21710  * @cfg {Boolean} bullets show bullets for the panels
21711  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21712  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21713  * @cfg {Boolean} showarrow (true|false) show arrow default true
21714  * 
21715  * @constructor
21716  * Create a new TabGroup
21717  * @param {Object} config The config object
21718  */
21719
21720 Roo.bootstrap.TabGroup = function(config){
21721     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21722     if (!this.navId) {
21723         this.navId = Roo.id();
21724     }
21725     this.tabs = [];
21726     Roo.bootstrap.TabGroup.register(this);
21727     
21728 };
21729
21730 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21731     
21732     carousel : false,
21733     transition : false,
21734     bullets : 0,
21735     timer : 0,
21736     autoslide : false,
21737     slideFn : false,
21738     slideOnTouch : false,
21739     showarrow : true,
21740     
21741     getAutoCreate : function()
21742     {
21743         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21744         
21745         cfg.cls += ' tab-content';
21746         
21747         if (this.carousel) {
21748             cfg.cls += ' carousel slide';
21749             
21750             cfg.cn = [{
21751                cls : 'carousel-inner',
21752                cn : []
21753             }];
21754         
21755             if(this.bullets  && !Roo.isTouch){
21756                 
21757                 var bullets = {
21758                     cls : 'carousel-bullets',
21759                     cn : []
21760                 };
21761                
21762                 if(this.bullets_cls){
21763                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21764                 }
21765                 
21766                 bullets.cn.push({
21767                     cls : 'clear'
21768                 });
21769                 
21770                 cfg.cn[0].cn.push(bullets);
21771             }
21772             
21773             if(this.showarrow){
21774                 cfg.cn[0].cn.push({
21775                     tag : 'div',
21776                     class : 'carousel-arrow',
21777                     cn : [
21778                         {
21779                             tag : 'div',
21780                             class : 'carousel-prev',
21781                             cn : [
21782                                 {
21783                                     tag : 'i',
21784                                     class : 'fa fa-chevron-left'
21785                                 }
21786                             ]
21787                         },
21788                         {
21789                             tag : 'div',
21790                             class : 'carousel-next',
21791                             cn : [
21792                                 {
21793                                     tag : 'i',
21794                                     class : 'fa fa-chevron-right'
21795                                 }
21796                             ]
21797                         }
21798                     ]
21799                 });
21800             }
21801             
21802         }
21803         
21804         return cfg;
21805     },
21806     
21807     initEvents:  function()
21808     {
21809 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21810 //            this.el.on("touchstart", this.onTouchStart, this);
21811 //        }
21812         
21813         if(this.autoslide){
21814             var _this = this;
21815             
21816             this.slideFn = window.setInterval(function() {
21817                 _this.showPanelNext();
21818             }, this.timer);
21819         }
21820         
21821         if(this.showarrow){
21822             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21823             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21824         }
21825         
21826         
21827     },
21828     
21829 //    onTouchStart : function(e, el, o)
21830 //    {
21831 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21832 //            return;
21833 //        }
21834 //        
21835 //        this.showPanelNext();
21836 //    },
21837     
21838     
21839     getChildContainer : function()
21840     {
21841         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21842     },
21843     
21844     /**
21845     * register a Navigation item
21846     * @param {Roo.bootstrap.NavItem} the navitem to add
21847     */
21848     register : function(item)
21849     {
21850         this.tabs.push( item);
21851         item.navId = this.navId; // not really needed..
21852         this.addBullet();
21853     
21854     },
21855     
21856     getActivePanel : function()
21857     {
21858         var r = false;
21859         Roo.each(this.tabs, function(t) {
21860             if (t.active) {
21861                 r = t;
21862                 return false;
21863             }
21864             return null;
21865         });
21866         return r;
21867         
21868     },
21869     getPanelByName : function(n)
21870     {
21871         var r = false;
21872         Roo.each(this.tabs, function(t) {
21873             if (t.tabId == n) {
21874                 r = t;
21875                 return false;
21876             }
21877             return null;
21878         });
21879         return r;
21880     },
21881     indexOfPanel : function(p)
21882     {
21883         var r = false;
21884         Roo.each(this.tabs, function(t,i) {
21885             if (t.tabId == p.tabId) {
21886                 r = i;
21887                 return false;
21888             }
21889             return null;
21890         });
21891         return r;
21892     },
21893     /**
21894      * show a specific panel
21895      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21896      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21897      */
21898     showPanel : function (pan)
21899     {
21900         if(this.transition || typeof(pan) == 'undefined'){
21901             Roo.log("waiting for the transitionend");
21902             return false;
21903         }
21904         
21905         if (typeof(pan) == 'number') {
21906             pan = this.tabs[pan];
21907         }
21908         
21909         if (typeof(pan) == 'string') {
21910             pan = this.getPanelByName(pan);
21911         }
21912         
21913         var cur = this.getActivePanel();
21914         
21915         if(!pan || !cur){
21916             Roo.log('pan or acitve pan is undefined');
21917             return false;
21918         }
21919         
21920         if (pan.tabId == this.getActivePanel().tabId) {
21921             return true;
21922         }
21923         
21924         if (false === cur.fireEvent('beforedeactivate')) {
21925             return false;
21926         }
21927         
21928         if(this.bullets > 0 && !Roo.isTouch){
21929             this.setActiveBullet(this.indexOfPanel(pan));
21930         }
21931         
21932         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21933             
21934             //class="carousel-item carousel-item-next carousel-item-left"
21935             
21936             this.transition = true;
21937             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21938             var lr = dir == 'next' ? 'left' : 'right';
21939             pan.el.addClass(dir); // or prev
21940             pan.el.addClass('carousel-item-' + dir); // or prev
21941             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21942             cur.el.addClass(lr); // or right
21943             pan.el.addClass(lr);
21944             cur.el.addClass('carousel-item-' +lr); // or right
21945             pan.el.addClass('carousel-item-' +lr);
21946             
21947             
21948             var _this = this;
21949             cur.el.on('transitionend', function() {
21950                 Roo.log("trans end?");
21951                 
21952                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21953                 pan.setActive(true);
21954                 
21955                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21956                 cur.setActive(false);
21957                 
21958                 _this.transition = false;
21959                 
21960             }, this, { single:  true } );
21961             
21962             return true;
21963         }
21964         
21965         cur.setActive(false);
21966         pan.setActive(true);
21967         
21968         return true;
21969         
21970     },
21971     showPanelNext : function()
21972     {
21973         var i = this.indexOfPanel(this.getActivePanel());
21974         
21975         if (i >= this.tabs.length - 1 && !this.autoslide) {
21976             return;
21977         }
21978         
21979         if (i >= this.tabs.length - 1 && this.autoslide) {
21980             i = -1;
21981         }
21982         
21983         this.showPanel(this.tabs[i+1]);
21984     },
21985     
21986     showPanelPrev : function()
21987     {
21988         var i = this.indexOfPanel(this.getActivePanel());
21989         
21990         if (i  < 1 && !this.autoslide) {
21991             return;
21992         }
21993         
21994         if (i < 1 && this.autoslide) {
21995             i = this.tabs.length;
21996         }
21997         
21998         this.showPanel(this.tabs[i-1]);
21999     },
22000     
22001     
22002     addBullet: function()
22003     {
22004         if(!this.bullets || Roo.isTouch){
22005             return;
22006         }
22007         var ctr = this.el.select('.carousel-bullets',true).first();
22008         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22009         var bullet = ctr.createChild({
22010             cls : 'bullet bullet-' + i
22011         },ctr.dom.lastChild);
22012         
22013         
22014         var _this = this;
22015         
22016         bullet.on('click', (function(e, el, o, ii, t){
22017
22018             e.preventDefault();
22019
22020             this.showPanel(ii);
22021
22022             if(this.autoslide && this.slideFn){
22023                 clearInterval(this.slideFn);
22024                 this.slideFn = window.setInterval(function() {
22025                     _this.showPanelNext();
22026                 }, this.timer);
22027             }
22028
22029         }).createDelegate(this, [i, bullet], true));
22030                 
22031         
22032     },
22033      
22034     setActiveBullet : function(i)
22035     {
22036         if(Roo.isTouch){
22037             return;
22038         }
22039         
22040         Roo.each(this.el.select('.bullet', true).elements, function(el){
22041             el.removeClass('selected');
22042         });
22043
22044         var bullet = this.el.select('.bullet-' + i, true).first();
22045         
22046         if(!bullet){
22047             return;
22048         }
22049         
22050         bullet.addClass('selected');
22051     }
22052     
22053     
22054   
22055 });
22056
22057  
22058
22059  
22060  
22061 Roo.apply(Roo.bootstrap.TabGroup, {
22062     
22063     groups: {},
22064      /**
22065     * register a Navigation Group
22066     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22067     */
22068     register : function(navgrp)
22069     {
22070         this.groups[navgrp.navId] = navgrp;
22071         
22072     },
22073     /**
22074     * fetch a Navigation Group based on the navigation ID
22075     * if one does not exist , it will get created.
22076     * @param {string} the navgroup to add
22077     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22078     */
22079     get: function(navId) {
22080         if (typeof(this.groups[navId]) == 'undefined') {
22081             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22082         }
22083         return this.groups[navId] ;
22084     }
22085     
22086     
22087     
22088 });
22089
22090  /*
22091  * - LGPL
22092  *
22093  * TabPanel
22094  * 
22095  */
22096
22097 /**
22098  * @class Roo.bootstrap.TabPanel
22099  * @extends Roo.bootstrap.Component
22100  * Bootstrap TabPanel class
22101  * @cfg {Boolean} active panel active
22102  * @cfg {String} html panel content
22103  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22104  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22105  * @cfg {String} href click to link..
22106  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22107  * 
22108  * 
22109  * @constructor
22110  * Create a new TabPanel
22111  * @param {Object} config The config object
22112  */
22113
22114 Roo.bootstrap.TabPanel = function(config){
22115     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22116     this.addEvents({
22117         /**
22118              * @event changed
22119              * Fires when the active status changes
22120              * @param {Roo.bootstrap.TabPanel} this
22121              * @param {Boolean} state the new state
22122             
22123          */
22124         'changed': true,
22125         /**
22126              * @event beforedeactivate
22127              * Fires before a tab is de-activated - can be used to do validation on a form.
22128              * @param {Roo.bootstrap.TabPanel} this
22129              * @return {Boolean} false if there is an error
22130             
22131          */
22132         'beforedeactivate': true
22133      });
22134     
22135     this.tabId = this.tabId || Roo.id();
22136   
22137 };
22138
22139 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22140     
22141     active: false,
22142     html: false,
22143     tabId: false,
22144     navId : false,
22145     href : '',
22146     touchSlide : false,
22147     getAutoCreate : function(){
22148         
22149         
22150         var cfg = {
22151             tag: 'div',
22152             // item is needed for carousel - not sure if it has any effect otherwise
22153             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22154             html: this.html || ''
22155         };
22156         
22157         if(this.active){
22158             cfg.cls += ' active';
22159         }
22160         
22161         if(this.tabId){
22162             cfg.tabId = this.tabId;
22163         }
22164         
22165         
22166         
22167         return cfg;
22168     },
22169     
22170     initEvents:  function()
22171     {
22172         var p = this.parent();
22173         
22174         this.navId = this.navId || p.navId;
22175         
22176         if (typeof(this.navId) != 'undefined') {
22177             // not really needed.. but just in case.. parent should be a NavGroup.
22178             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22179             
22180             tg.register(this);
22181             
22182             var i = tg.tabs.length - 1;
22183             
22184             if(this.active && tg.bullets > 0 && i < tg.bullets){
22185                 tg.setActiveBullet(i);
22186             }
22187         }
22188         
22189         this.el.on('click', this.onClick, this);
22190         
22191         if(Roo.isTouch && this.touchSlide){
22192             this.el.on("touchstart", this.onTouchStart, this);
22193             this.el.on("touchmove", this.onTouchMove, this);
22194             this.el.on("touchend", this.onTouchEnd, this);
22195         }
22196         
22197     },
22198     
22199     onRender : function(ct, position)
22200     {
22201         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22202     },
22203     
22204     setActive : function(state)
22205     {
22206         Roo.log("panel - set active " + this.tabId + "=" + state);
22207         
22208         this.active = state;
22209         if (!state) {
22210             this.el.removeClass('active');
22211             
22212         } else  if (!this.el.hasClass('active')) {
22213             this.el.addClass('active');
22214         }
22215         
22216         this.fireEvent('changed', this, state);
22217     },
22218     
22219     onClick : function(e)
22220     {
22221         e.preventDefault();
22222         
22223         if(!this.href.length){
22224             return;
22225         }
22226         
22227         window.location.href = this.href;
22228     },
22229     
22230     startX : 0,
22231     startY : 0,
22232     endX : 0,
22233     endY : 0,
22234     swiping : false,
22235     
22236     onTouchStart : function(e)
22237     {
22238         this.swiping = false;
22239         
22240         this.startX = e.browserEvent.touches[0].clientX;
22241         this.startY = e.browserEvent.touches[0].clientY;
22242     },
22243     
22244     onTouchMove : function(e)
22245     {
22246         this.swiping = true;
22247         
22248         this.endX = e.browserEvent.touches[0].clientX;
22249         this.endY = e.browserEvent.touches[0].clientY;
22250     },
22251     
22252     onTouchEnd : function(e)
22253     {
22254         if(!this.swiping){
22255             this.onClick(e);
22256             return;
22257         }
22258         
22259         var tabGroup = this.parent();
22260         
22261         if(this.endX > this.startX){ // swiping right
22262             tabGroup.showPanelPrev();
22263             return;
22264         }
22265         
22266         if(this.startX > this.endX){ // swiping left
22267             tabGroup.showPanelNext();
22268             return;
22269         }
22270     }
22271     
22272     
22273 });
22274  
22275
22276  
22277
22278  /*
22279  * - LGPL
22280  *
22281  * DateField
22282  * 
22283  */
22284
22285 /**
22286  * @class Roo.bootstrap.DateField
22287  * @extends Roo.bootstrap.Input
22288  * Bootstrap DateField class
22289  * @cfg {Number} weekStart default 0
22290  * @cfg {String} viewMode default empty, (months|years)
22291  * @cfg {String} minViewMode default empty, (months|years)
22292  * @cfg {Number} startDate default -Infinity
22293  * @cfg {Number} endDate default Infinity
22294  * @cfg {Boolean} todayHighlight default false
22295  * @cfg {Boolean} todayBtn default false
22296  * @cfg {Boolean} calendarWeeks default false
22297  * @cfg {Object} daysOfWeekDisabled default empty
22298  * @cfg {Boolean} singleMode default false (true | false)
22299  * 
22300  * @cfg {Boolean} keyboardNavigation default true
22301  * @cfg {String} language default en
22302  * 
22303  * @constructor
22304  * Create a new DateField
22305  * @param {Object} config The config object
22306  */
22307
22308 Roo.bootstrap.DateField = function(config){
22309     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22310      this.addEvents({
22311             /**
22312              * @event show
22313              * Fires when this field show.
22314              * @param {Roo.bootstrap.DateField} this
22315              * @param {Mixed} date The date value
22316              */
22317             show : true,
22318             /**
22319              * @event show
22320              * Fires when this field hide.
22321              * @param {Roo.bootstrap.DateField} this
22322              * @param {Mixed} date The date value
22323              */
22324             hide : true,
22325             /**
22326              * @event select
22327              * Fires when select a date.
22328              * @param {Roo.bootstrap.DateField} this
22329              * @param {Mixed} date The date value
22330              */
22331             select : true,
22332             /**
22333              * @event beforeselect
22334              * Fires when before select a date.
22335              * @param {Roo.bootstrap.DateField} this
22336              * @param {Mixed} date The date value
22337              */
22338             beforeselect : true
22339         });
22340 };
22341
22342 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22343     
22344     /**
22345      * @cfg {String} format
22346      * The default date format string which can be overriden for localization support.  The format must be
22347      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22348      */
22349     format : "m/d/y",
22350     /**
22351      * @cfg {String} altFormats
22352      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22353      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22354      */
22355     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22356     
22357     weekStart : 0,
22358     
22359     viewMode : '',
22360     
22361     minViewMode : '',
22362     
22363     todayHighlight : false,
22364     
22365     todayBtn: false,
22366     
22367     language: 'en',
22368     
22369     keyboardNavigation: true,
22370     
22371     calendarWeeks: false,
22372     
22373     startDate: -Infinity,
22374     
22375     endDate: Infinity,
22376     
22377     daysOfWeekDisabled: [],
22378     
22379     _events: [],
22380     
22381     singleMode : false,
22382     
22383     UTCDate: function()
22384     {
22385         return new Date(Date.UTC.apply(Date, arguments));
22386     },
22387     
22388     UTCToday: function()
22389     {
22390         var today = new Date();
22391         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22392     },
22393     
22394     getDate: function() {
22395             var d = this.getUTCDate();
22396             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22397     },
22398     
22399     getUTCDate: function() {
22400             return this.date;
22401     },
22402     
22403     setDate: function(d) {
22404             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22405     },
22406     
22407     setUTCDate: function(d) {
22408             this.date = d;
22409             this.setValue(this.formatDate(this.date));
22410     },
22411         
22412     onRender: function(ct, position)
22413     {
22414         
22415         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22416         
22417         this.language = this.language || 'en';
22418         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22419         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22420         
22421         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22422         this.format = this.format || 'm/d/y';
22423         this.isInline = false;
22424         this.isInput = true;
22425         this.component = this.el.select('.add-on', true).first() || false;
22426         this.component = (this.component && this.component.length === 0) ? false : this.component;
22427         this.hasInput = this.component && this.inputEl().length;
22428         
22429         if (typeof(this.minViewMode === 'string')) {
22430             switch (this.minViewMode) {
22431                 case 'months':
22432                     this.minViewMode = 1;
22433                     break;
22434                 case 'years':
22435                     this.minViewMode = 2;
22436                     break;
22437                 default:
22438                     this.minViewMode = 0;
22439                     break;
22440             }
22441         }
22442         
22443         if (typeof(this.viewMode === 'string')) {
22444             switch (this.viewMode) {
22445                 case 'months':
22446                     this.viewMode = 1;
22447                     break;
22448                 case 'years':
22449                     this.viewMode = 2;
22450                     break;
22451                 default:
22452                     this.viewMode = 0;
22453                     break;
22454             }
22455         }
22456                 
22457         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22458         
22459 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22460         
22461         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22462         
22463         this.picker().on('mousedown', this.onMousedown, this);
22464         this.picker().on('click', this.onClick, this);
22465         
22466         this.picker().addClass('datepicker-dropdown');
22467         
22468         this.startViewMode = this.viewMode;
22469         
22470         if(this.singleMode){
22471             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22472                 v.setVisibilityMode(Roo.Element.DISPLAY);
22473                 v.hide();
22474             });
22475             
22476             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22477                 v.setStyle('width', '189px');
22478             });
22479         }
22480         
22481         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22482             if(!this.calendarWeeks){
22483                 v.remove();
22484                 return;
22485             }
22486             
22487             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22488             v.attr('colspan', function(i, val){
22489                 return parseInt(val) + 1;
22490             });
22491         });
22492                         
22493         
22494         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22495         
22496         this.setStartDate(this.startDate);
22497         this.setEndDate(this.endDate);
22498         
22499         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22500         
22501         this.fillDow();
22502         this.fillMonths();
22503         this.update();
22504         this.showMode();
22505         
22506         if(this.isInline) {
22507             this.showPopup();
22508         }
22509     },
22510     
22511     picker : function()
22512     {
22513         return this.pickerEl;
22514 //        return this.el.select('.datepicker', true).first();
22515     },
22516     
22517     fillDow: function()
22518     {
22519         var dowCnt = this.weekStart;
22520         
22521         var dow = {
22522             tag: 'tr',
22523             cn: [
22524                 
22525             ]
22526         };
22527         
22528         if(this.calendarWeeks){
22529             dow.cn.push({
22530                 tag: 'th',
22531                 cls: 'cw',
22532                 html: '&nbsp;'
22533             })
22534         }
22535         
22536         while (dowCnt < this.weekStart + 7) {
22537             dow.cn.push({
22538                 tag: 'th',
22539                 cls: 'dow',
22540                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22541             });
22542         }
22543         
22544         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22545     },
22546     
22547     fillMonths: function()
22548     {    
22549         var i = 0;
22550         var months = this.picker().select('>.datepicker-months td', true).first();
22551         
22552         months.dom.innerHTML = '';
22553         
22554         while (i < 12) {
22555             var month = {
22556                 tag: 'span',
22557                 cls: 'month',
22558                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22559             };
22560             
22561             months.createChild(month);
22562         }
22563         
22564     },
22565     
22566     update: function()
22567     {
22568         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;
22569         
22570         if (this.date < this.startDate) {
22571             this.viewDate = new Date(this.startDate);
22572         } else if (this.date > this.endDate) {
22573             this.viewDate = new Date(this.endDate);
22574         } else {
22575             this.viewDate = new Date(this.date);
22576         }
22577         
22578         this.fill();
22579     },
22580     
22581     fill: function() 
22582     {
22583         var d = new Date(this.viewDate),
22584                 year = d.getUTCFullYear(),
22585                 month = d.getUTCMonth(),
22586                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22587                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22588                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22589                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22590                 currentDate = this.date && this.date.valueOf(),
22591                 today = this.UTCToday();
22592         
22593         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22594         
22595 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22596         
22597 //        this.picker.select('>tfoot th.today').
22598 //                                              .text(dates[this.language].today)
22599 //                                              .toggle(this.todayBtn !== false);
22600     
22601         this.updateNavArrows();
22602         this.fillMonths();
22603                                                 
22604         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22605         
22606         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22607          
22608         prevMonth.setUTCDate(day);
22609         
22610         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22611         
22612         var nextMonth = new Date(prevMonth);
22613         
22614         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22615         
22616         nextMonth = nextMonth.valueOf();
22617         
22618         var fillMonths = false;
22619         
22620         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22621         
22622         while(prevMonth.valueOf() <= nextMonth) {
22623             var clsName = '';
22624             
22625             if (prevMonth.getUTCDay() === this.weekStart) {
22626                 if(fillMonths){
22627                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22628                 }
22629                     
22630                 fillMonths = {
22631                     tag: 'tr',
22632                     cn: []
22633                 };
22634                 
22635                 if(this.calendarWeeks){
22636                     // ISO 8601: First week contains first thursday.
22637                     // ISO also states week starts on Monday, but we can be more abstract here.
22638                     var
22639                     // Start of current week: based on weekstart/current date
22640                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22641                     // Thursday of this week
22642                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22643                     // First Thursday of year, year from thursday
22644                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22645                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22646                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22647                     
22648                     fillMonths.cn.push({
22649                         tag: 'td',
22650                         cls: 'cw',
22651                         html: calWeek
22652                     });
22653                 }
22654             }
22655             
22656             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22657                 clsName += ' old';
22658             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22659                 clsName += ' new';
22660             }
22661             if (this.todayHighlight &&
22662                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22663                 prevMonth.getUTCMonth() == today.getMonth() &&
22664                 prevMonth.getUTCDate() == today.getDate()) {
22665                 clsName += ' today';
22666             }
22667             
22668             if (currentDate && prevMonth.valueOf() === currentDate) {
22669                 clsName += ' active';
22670             }
22671             
22672             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22673                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22674                     clsName += ' disabled';
22675             }
22676             
22677             fillMonths.cn.push({
22678                 tag: 'td',
22679                 cls: 'day ' + clsName,
22680                 html: prevMonth.getDate()
22681             });
22682             
22683             prevMonth.setDate(prevMonth.getDate()+1);
22684         }
22685           
22686         var currentYear = this.date && this.date.getUTCFullYear();
22687         var currentMonth = this.date && this.date.getUTCMonth();
22688         
22689         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22690         
22691         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22692             v.removeClass('active');
22693             
22694             if(currentYear === year && k === currentMonth){
22695                 v.addClass('active');
22696             }
22697             
22698             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22699                 v.addClass('disabled');
22700             }
22701             
22702         });
22703         
22704         
22705         year = parseInt(year/10, 10) * 10;
22706         
22707         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22708         
22709         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22710         
22711         year -= 1;
22712         for (var i = -1; i < 11; i++) {
22713             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22714                 tag: 'span',
22715                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22716                 html: year
22717             });
22718             
22719             year += 1;
22720         }
22721     },
22722     
22723     showMode: function(dir) 
22724     {
22725         if (dir) {
22726             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22727         }
22728         
22729         Roo.each(this.picker().select('>div',true).elements, function(v){
22730             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22731             v.hide();
22732         });
22733         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22734     },
22735     
22736     place: function()
22737     {
22738         if(this.isInline) {
22739             return;
22740         }
22741         
22742         this.picker().removeClass(['bottom', 'top']);
22743         
22744         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22745             /*
22746              * place to the top of element!
22747              *
22748              */
22749             
22750             this.picker().addClass('top');
22751             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22752             
22753             return;
22754         }
22755         
22756         this.picker().addClass('bottom');
22757         
22758         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22759     },
22760     
22761     parseDate : function(value)
22762     {
22763         if(!value || value instanceof Date){
22764             return value;
22765         }
22766         var v = Date.parseDate(value, this.format);
22767         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22768             v = Date.parseDate(value, 'Y-m-d');
22769         }
22770         if(!v && this.altFormats){
22771             if(!this.altFormatsArray){
22772                 this.altFormatsArray = this.altFormats.split("|");
22773             }
22774             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22775                 v = Date.parseDate(value, this.altFormatsArray[i]);
22776             }
22777         }
22778         return v;
22779     },
22780     
22781     formatDate : function(date, fmt)
22782     {   
22783         return (!date || !(date instanceof Date)) ?
22784         date : date.dateFormat(fmt || this.format);
22785     },
22786     
22787     onFocus : function()
22788     {
22789         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22790         this.showPopup();
22791     },
22792     
22793     onBlur : function()
22794     {
22795         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22796         
22797         var d = this.inputEl().getValue();
22798         
22799         this.setValue(d);
22800                 
22801         this.hidePopup();
22802     },
22803     
22804     showPopup : function()
22805     {
22806         this.picker().show();
22807         this.update();
22808         this.place();
22809         
22810         this.fireEvent('showpopup', this, this.date);
22811     },
22812     
22813     hidePopup : function()
22814     {
22815         if(this.isInline) {
22816             return;
22817         }
22818         this.picker().hide();
22819         this.viewMode = this.startViewMode;
22820         this.showMode();
22821         
22822         this.fireEvent('hidepopup', this, this.date);
22823         
22824     },
22825     
22826     onMousedown: function(e)
22827     {
22828         e.stopPropagation();
22829         e.preventDefault();
22830     },
22831     
22832     keyup: function(e)
22833     {
22834         Roo.bootstrap.DateField.superclass.keyup.call(this);
22835         this.update();
22836     },
22837
22838     setValue: function(v)
22839     {
22840         if(this.fireEvent('beforeselect', this, v) !== false){
22841             var d = new Date(this.parseDate(v) ).clearTime();
22842         
22843             if(isNaN(d.getTime())){
22844                 this.date = this.viewDate = '';
22845                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22846                 return;
22847             }
22848
22849             v = this.formatDate(d);
22850
22851             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22852
22853             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22854
22855             this.update();
22856
22857             this.fireEvent('select', this, this.date);
22858         }
22859     },
22860     
22861     getValue: function()
22862     {
22863         return this.formatDate(this.date);
22864     },
22865     
22866     fireKey: function(e)
22867     {
22868         if (!this.picker().isVisible()){
22869             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22870                 this.showPopup();
22871             }
22872             return;
22873         }
22874         
22875         var dateChanged = false,
22876         dir, day, month,
22877         newDate, newViewDate;
22878         
22879         switch(e.keyCode){
22880             case 27: // escape
22881                 this.hidePopup();
22882                 e.preventDefault();
22883                 break;
22884             case 37: // left
22885             case 39: // right
22886                 if (!this.keyboardNavigation) {
22887                     break;
22888                 }
22889                 dir = e.keyCode == 37 ? -1 : 1;
22890                 
22891                 if (e.ctrlKey){
22892                     newDate = this.moveYear(this.date, dir);
22893                     newViewDate = this.moveYear(this.viewDate, dir);
22894                 } else if (e.shiftKey){
22895                     newDate = this.moveMonth(this.date, dir);
22896                     newViewDate = this.moveMonth(this.viewDate, dir);
22897                 } else {
22898                     newDate = new Date(this.date);
22899                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22900                     newViewDate = new Date(this.viewDate);
22901                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22902                 }
22903                 if (this.dateWithinRange(newDate)){
22904                     this.date = newDate;
22905                     this.viewDate = newViewDate;
22906                     this.setValue(this.formatDate(this.date));
22907 //                    this.update();
22908                     e.preventDefault();
22909                     dateChanged = true;
22910                 }
22911                 break;
22912             case 38: // up
22913             case 40: // down
22914                 if (!this.keyboardNavigation) {
22915                     break;
22916                 }
22917                 dir = e.keyCode == 38 ? -1 : 1;
22918                 if (e.ctrlKey){
22919                     newDate = this.moveYear(this.date, dir);
22920                     newViewDate = this.moveYear(this.viewDate, dir);
22921                 } else if (e.shiftKey){
22922                     newDate = this.moveMonth(this.date, dir);
22923                     newViewDate = this.moveMonth(this.viewDate, dir);
22924                 } else {
22925                     newDate = new Date(this.date);
22926                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22927                     newViewDate = new Date(this.viewDate);
22928                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22929                 }
22930                 if (this.dateWithinRange(newDate)){
22931                     this.date = newDate;
22932                     this.viewDate = newViewDate;
22933                     this.setValue(this.formatDate(this.date));
22934 //                    this.update();
22935                     e.preventDefault();
22936                     dateChanged = true;
22937                 }
22938                 break;
22939             case 13: // enter
22940                 this.setValue(this.formatDate(this.date));
22941                 this.hidePopup();
22942                 e.preventDefault();
22943                 break;
22944             case 9: // tab
22945                 this.setValue(this.formatDate(this.date));
22946                 this.hidePopup();
22947                 break;
22948             case 16: // shift
22949             case 17: // ctrl
22950             case 18: // alt
22951                 break;
22952             default :
22953                 this.hidePopup();
22954                 
22955         }
22956     },
22957     
22958     
22959     onClick: function(e) 
22960     {
22961         e.stopPropagation();
22962         e.preventDefault();
22963         
22964         var target = e.getTarget();
22965         
22966         if(target.nodeName.toLowerCase() === 'i'){
22967             target = Roo.get(target).dom.parentNode;
22968         }
22969         
22970         var nodeName = target.nodeName;
22971         var className = target.className;
22972         var html = target.innerHTML;
22973         //Roo.log(nodeName);
22974         
22975         switch(nodeName.toLowerCase()) {
22976             case 'th':
22977                 switch(className) {
22978                     case 'switch':
22979                         this.showMode(1);
22980                         break;
22981                     case 'prev':
22982                     case 'next':
22983                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22984                         switch(this.viewMode){
22985                                 case 0:
22986                                         this.viewDate = this.moveMonth(this.viewDate, dir);
22987                                         break;
22988                                 case 1:
22989                                 case 2:
22990                                         this.viewDate = this.moveYear(this.viewDate, dir);
22991                                         break;
22992                         }
22993                         this.fill();
22994                         break;
22995                     case 'today':
22996                         var date = new Date();
22997                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22998 //                        this.fill()
22999                         this.setValue(this.formatDate(this.date));
23000                         
23001                         this.hidePopup();
23002                         break;
23003                 }
23004                 break;
23005             case 'span':
23006                 if (className.indexOf('disabled') < 0) {
23007                 if (!this.viewDate) {
23008                     this.viewDate = new Date();
23009                 }
23010                 this.viewDate.setUTCDate(1);
23011                     if (className.indexOf('month') > -1) {
23012                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23013                     } else {
23014                         var year = parseInt(html, 10) || 0;
23015                         this.viewDate.setUTCFullYear(year);
23016                         
23017                     }
23018                     
23019                     if(this.singleMode){
23020                         this.setValue(this.formatDate(this.viewDate));
23021                         this.hidePopup();
23022                         return;
23023                     }
23024                     
23025                     this.showMode(-1);
23026                     this.fill();
23027                 }
23028                 break;
23029                 
23030             case 'td':
23031                 //Roo.log(className);
23032                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23033                     var day = parseInt(html, 10) || 1;
23034                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23035                         month = (this.viewDate || new Date()).getUTCMonth();
23036
23037                     if (className.indexOf('old') > -1) {
23038                         if(month === 0 ){
23039                             month = 11;
23040                             year -= 1;
23041                         }else{
23042                             month -= 1;
23043                         }
23044                     } else if (className.indexOf('new') > -1) {
23045                         if (month == 11) {
23046                             month = 0;
23047                             year += 1;
23048                         } else {
23049                             month += 1;
23050                         }
23051                     }
23052                     //Roo.log([year,month,day]);
23053                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23054                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23055 //                    this.fill();
23056                     //Roo.log(this.formatDate(this.date));
23057                     this.setValue(this.formatDate(this.date));
23058                     this.hidePopup();
23059                 }
23060                 break;
23061         }
23062     },
23063     
23064     setStartDate: function(startDate)
23065     {
23066         this.startDate = startDate || -Infinity;
23067         if (this.startDate !== -Infinity) {
23068             this.startDate = this.parseDate(this.startDate);
23069         }
23070         this.update();
23071         this.updateNavArrows();
23072     },
23073
23074     setEndDate: function(endDate)
23075     {
23076         this.endDate = endDate || Infinity;
23077         if (this.endDate !== Infinity) {
23078             this.endDate = this.parseDate(this.endDate);
23079         }
23080         this.update();
23081         this.updateNavArrows();
23082     },
23083     
23084     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23085     {
23086         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23087         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23088             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23089         }
23090         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23091             return parseInt(d, 10);
23092         });
23093         this.update();
23094         this.updateNavArrows();
23095     },
23096     
23097     updateNavArrows: function() 
23098     {
23099         if(this.singleMode){
23100             return;
23101         }
23102         
23103         var d = new Date(this.viewDate),
23104         year = d.getUTCFullYear(),
23105         month = d.getUTCMonth();
23106         
23107         Roo.each(this.picker().select('.prev', true).elements, function(v){
23108             v.show();
23109             switch (this.viewMode) {
23110                 case 0:
23111
23112                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23113                         v.hide();
23114                     }
23115                     break;
23116                 case 1:
23117                 case 2:
23118                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23119                         v.hide();
23120                     }
23121                     break;
23122             }
23123         });
23124         
23125         Roo.each(this.picker().select('.next', true).elements, function(v){
23126             v.show();
23127             switch (this.viewMode) {
23128                 case 0:
23129
23130                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23131                         v.hide();
23132                     }
23133                     break;
23134                 case 1:
23135                 case 2:
23136                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23137                         v.hide();
23138                     }
23139                     break;
23140             }
23141         })
23142     },
23143     
23144     moveMonth: function(date, dir)
23145     {
23146         if (!dir) {
23147             return date;
23148         }
23149         var new_date = new Date(date.valueOf()),
23150         day = new_date.getUTCDate(),
23151         month = new_date.getUTCMonth(),
23152         mag = Math.abs(dir),
23153         new_month, test;
23154         dir = dir > 0 ? 1 : -1;
23155         if (mag == 1){
23156             test = dir == -1
23157             // If going back one month, make sure month is not current month
23158             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23159             ? function(){
23160                 return new_date.getUTCMonth() == month;
23161             }
23162             // If going forward one month, make sure month is as expected
23163             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23164             : function(){
23165                 return new_date.getUTCMonth() != new_month;
23166             };
23167             new_month = month + dir;
23168             new_date.setUTCMonth(new_month);
23169             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23170             if (new_month < 0 || new_month > 11) {
23171                 new_month = (new_month + 12) % 12;
23172             }
23173         } else {
23174             // For magnitudes >1, move one month at a time...
23175             for (var i=0; i<mag; i++) {
23176                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23177                 new_date = this.moveMonth(new_date, dir);
23178             }
23179             // ...then reset the day, keeping it in the new month
23180             new_month = new_date.getUTCMonth();
23181             new_date.setUTCDate(day);
23182             test = function(){
23183                 return new_month != new_date.getUTCMonth();
23184             };
23185         }
23186         // Common date-resetting loop -- if date is beyond end of month, make it
23187         // end of month
23188         while (test()){
23189             new_date.setUTCDate(--day);
23190             new_date.setUTCMonth(new_month);
23191         }
23192         return new_date;
23193     },
23194
23195     moveYear: function(date, dir)
23196     {
23197         return this.moveMonth(date, dir*12);
23198     },
23199
23200     dateWithinRange: function(date)
23201     {
23202         return date >= this.startDate && date <= this.endDate;
23203     },
23204
23205     
23206     remove: function() 
23207     {
23208         this.picker().remove();
23209     },
23210     
23211     validateValue : function(value)
23212     {
23213         if(this.getVisibilityEl().hasClass('hidden')){
23214             return true;
23215         }
23216         
23217         if(value.length < 1)  {
23218             if(this.allowBlank){
23219                 return true;
23220             }
23221             return false;
23222         }
23223         
23224         if(value.length < this.minLength){
23225             return false;
23226         }
23227         if(value.length > this.maxLength){
23228             return false;
23229         }
23230         if(this.vtype){
23231             var vt = Roo.form.VTypes;
23232             if(!vt[this.vtype](value, this)){
23233                 return false;
23234             }
23235         }
23236         if(typeof this.validator == "function"){
23237             var msg = this.validator(value);
23238             if(msg !== true){
23239                 return false;
23240             }
23241         }
23242         
23243         if(this.regex && !this.regex.test(value)){
23244             return false;
23245         }
23246         
23247         if(typeof(this.parseDate(value)) == 'undefined'){
23248             return false;
23249         }
23250         
23251         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23252             return false;
23253         }      
23254         
23255         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23256             return false;
23257         } 
23258         
23259         
23260         return true;
23261     },
23262     
23263     reset : function()
23264     {
23265         this.date = this.viewDate = '';
23266         
23267         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23268     }
23269    
23270 });
23271
23272 Roo.apply(Roo.bootstrap.DateField,  {
23273     
23274     head : {
23275         tag: 'thead',
23276         cn: [
23277         {
23278             tag: 'tr',
23279             cn: [
23280             {
23281                 tag: 'th',
23282                 cls: 'prev',
23283                 html: '<i class="fa fa-arrow-left"/>'
23284             },
23285             {
23286                 tag: 'th',
23287                 cls: 'switch',
23288                 colspan: '5'
23289             },
23290             {
23291                 tag: 'th',
23292                 cls: 'next',
23293                 html: '<i class="fa fa-arrow-right"/>'
23294             }
23295
23296             ]
23297         }
23298         ]
23299     },
23300     
23301     content : {
23302         tag: 'tbody',
23303         cn: [
23304         {
23305             tag: 'tr',
23306             cn: [
23307             {
23308                 tag: 'td',
23309                 colspan: '7'
23310             }
23311             ]
23312         }
23313         ]
23314     },
23315     
23316     footer : {
23317         tag: 'tfoot',
23318         cn: [
23319         {
23320             tag: 'tr',
23321             cn: [
23322             {
23323                 tag: 'th',
23324                 colspan: '7',
23325                 cls: 'today'
23326             }
23327                     
23328             ]
23329         }
23330         ]
23331     },
23332     
23333     dates:{
23334         en: {
23335             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23336             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23337             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23338             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23339             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23340             today: "Today"
23341         }
23342     },
23343     
23344     modes: [
23345     {
23346         clsName: 'days',
23347         navFnc: 'Month',
23348         navStep: 1
23349     },
23350     {
23351         clsName: 'months',
23352         navFnc: 'FullYear',
23353         navStep: 1
23354     },
23355     {
23356         clsName: 'years',
23357         navFnc: 'FullYear',
23358         navStep: 10
23359     }]
23360 });
23361
23362 Roo.apply(Roo.bootstrap.DateField,  {
23363   
23364     template : {
23365         tag: 'div',
23366         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23367         cn: [
23368         {
23369             tag: 'div',
23370             cls: 'datepicker-days',
23371             cn: [
23372             {
23373                 tag: 'table',
23374                 cls: 'table-condensed',
23375                 cn:[
23376                 Roo.bootstrap.DateField.head,
23377                 {
23378                     tag: 'tbody'
23379                 },
23380                 Roo.bootstrap.DateField.footer
23381                 ]
23382             }
23383             ]
23384         },
23385         {
23386             tag: 'div',
23387             cls: 'datepicker-months',
23388             cn: [
23389             {
23390                 tag: 'table',
23391                 cls: 'table-condensed',
23392                 cn:[
23393                 Roo.bootstrap.DateField.head,
23394                 Roo.bootstrap.DateField.content,
23395                 Roo.bootstrap.DateField.footer
23396                 ]
23397             }
23398             ]
23399         },
23400         {
23401             tag: 'div',
23402             cls: 'datepicker-years',
23403             cn: [
23404             {
23405                 tag: 'table',
23406                 cls: 'table-condensed',
23407                 cn:[
23408                 Roo.bootstrap.DateField.head,
23409                 Roo.bootstrap.DateField.content,
23410                 Roo.bootstrap.DateField.footer
23411                 ]
23412             }
23413             ]
23414         }
23415         ]
23416     }
23417 });
23418
23419  
23420
23421  /*
23422  * - LGPL
23423  *
23424  * TimeField
23425  * 
23426  */
23427
23428 /**
23429  * @class Roo.bootstrap.TimeField
23430  * @extends Roo.bootstrap.Input
23431  * Bootstrap DateField class
23432  * 
23433  * 
23434  * @constructor
23435  * Create a new TimeField
23436  * @param {Object} config The config object
23437  */
23438
23439 Roo.bootstrap.TimeField = function(config){
23440     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23441     this.addEvents({
23442             /**
23443              * @event show
23444              * Fires when this field show.
23445              * @param {Roo.bootstrap.DateField} thisthis
23446              * @param {Mixed} date The date value
23447              */
23448             show : true,
23449             /**
23450              * @event show
23451              * Fires when this field hide.
23452              * @param {Roo.bootstrap.DateField} this
23453              * @param {Mixed} date The date value
23454              */
23455             hide : true,
23456             /**
23457              * @event select
23458              * Fires when select a date.
23459              * @param {Roo.bootstrap.DateField} this
23460              * @param {Mixed} date The date value
23461              */
23462             select : true
23463         });
23464 };
23465
23466 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23467     
23468     /**
23469      * @cfg {String} format
23470      * The default time format string which can be overriden for localization support.  The format must be
23471      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23472      */
23473     format : "H:i",
23474
23475     getAutoCreate : function()
23476     {
23477         this.after = '<i class="fa far fa-clock"></i>';
23478         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23479         
23480          
23481     },
23482     onRender: function(ct, position)
23483     {
23484         
23485         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23486                 
23487         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23488         
23489         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23490         
23491         this.pop = this.picker().select('>.datepicker-time',true).first();
23492         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23493         
23494         this.picker().on('mousedown', this.onMousedown, this);
23495         this.picker().on('click', this.onClick, this);
23496         
23497         this.picker().addClass('datepicker-dropdown');
23498     
23499         this.fillTime();
23500         this.update();
23501             
23502         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23503         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23504         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23505         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23506         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23507         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23508
23509     },
23510     
23511     fireKey: function(e){
23512         if (!this.picker().isVisible()){
23513             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23514                 this.show();
23515             }
23516             return;
23517         }
23518
23519         e.preventDefault();
23520         
23521         switch(e.keyCode){
23522             case 27: // escape
23523                 this.hide();
23524                 break;
23525             case 37: // left
23526             case 39: // right
23527                 this.onTogglePeriod();
23528                 break;
23529             case 38: // up
23530                 this.onIncrementMinutes();
23531                 break;
23532             case 40: // down
23533                 this.onDecrementMinutes();
23534                 break;
23535             case 13: // enter
23536             case 9: // tab
23537                 this.setTime();
23538                 break;
23539         }
23540     },
23541     
23542     onClick: function(e) {
23543         e.stopPropagation();
23544         e.preventDefault();
23545     },
23546     
23547     picker : function()
23548     {
23549         return this.pickerEl;
23550     },
23551     
23552     fillTime: function()
23553     {    
23554         var time = this.pop.select('tbody', true).first();
23555         
23556         time.dom.innerHTML = '';
23557         
23558         time.createChild({
23559             tag: 'tr',
23560             cn: [
23561                 {
23562                     tag: 'td',
23563                     cn: [
23564                         {
23565                             tag: 'a',
23566                             href: '#',
23567                             cls: 'btn',
23568                             cn: [
23569                                 {
23570                                     tag: 'i',
23571                                     cls: 'hours-up fa fas fa-chevron-up'
23572                                 }
23573                             ]
23574                         } 
23575                     ]
23576                 },
23577                 {
23578                     tag: 'td',
23579                     cls: 'separator'
23580                 },
23581                 {
23582                     tag: 'td',
23583                     cn: [
23584                         {
23585                             tag: 'a',
23586                             href: '#',
23587                             cls: 'btn',
23588                             cn: [
23589                                 {
23590                                     tag: 'i',
23591                                     cls: 'minutes-up fa fas fa-chevron-up'
23592                                 }
23593                             ]
23594                         }
23595                     ]
23596                 },
23597                 {
23598                     tag: 'td',
23599                     cls: 'separator'
23600                 }
23601             ]
23602         });
23603         
23604         time.createChild({
23605             tag: 'tr',
23606             cn: [
23607                 {
23608                     tag: 'td',
23609                     cn: [
23610                         {
23611                             tag: 'span',
23612                             cls: 'timepicker-hour',
23613                             html: '00'
23614                         }  
23615                     ]
23616                 },
23617                 {
23618                     tag: 'td',
23619                     cls: 'separator',
23620                     html: ':'
23621                 },
23622                 {
23623                     tag: 'td',
23624                     cn: [
23625                         {
23626                             tag: 'span',
23627                             cls: 'timepicker-minute',
23628                             html: '00'
23629                         }  
23630                     ]
23631                 },
23632                 {
23633                     tag: 'td',
23634                     cls: 'separator'
23635                 },
23636                 {
23637                     tag: 'td',
23638                     cn: [
23639                         {
23640                             tag: 'button',
23641                             type: 'button',
23642                             cls: 'btn btn-primary period',
23643                             html: 'AM'
23644                             
23645                         }
23646                     ]
23647                 }
23648             ]
23649         });
23650         
23651         time.createChild({
23652             tag: 'tr',
23653             cn: [
23654                 {
23655                     tag: 'td',
23656                     cn: [
23657                         {
23658                             tag: 'a',
23659                             href: '#',
23660                             cls: 'btn',
23661                             cn: [
23662                                 {
23663                                     tag: 'span',
23664                                     cls: 'hours-down fa fas fa-chevron-down'
23665                                 }
23666                             ]
23667                         }
23668                     ]
23669                 },
23670                 {
23671                     tag: 'td',
23672                     cls: 'separator'
23673                 },
23674                 {
23675                     tag: 'td',
23676                     cn: [
23677                         {
23678                             tag: 'a',
23679                             href: '#',
23680                             cls: 'btn',
23681                             cn: [
23682                                 {
23683                                     tag: 'span',
23684                                     cls: 'minutes-down fa fas fa-chevron-down'
23685                                 }
23686                             ]
23687                         }
23688                     ]
23689                 },
23690                 {
23691                     tag: 'td',
23692                     cls: 'separator'
23693                 }
23694             ]
23695         });
23696         
23697     },
23698     
23699     update: function()
23700     {
23701         
23702         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23703         
23704         this.fill();
23705     },
23706     
23707     fill: function() 
23708     {
23709         var hours = this.time.getHours();
23710         var minutes = this.time.getMinutes();
23711         var period = 'AM';
23712         
23713         if(hours > 11){
23714             period = 'PM';
23715         }
23716         
23717         if(hours == 0){
23718             hours = 12;
23719         }
23720         
23721         
23722         if(hours > 12){
23723             hours = hours - 12;
23724         }
23725         
23726         if(hours < 10){
23727             hours = '0' + hours;
23728         }
23729         
23730         if(minutes < 10){
23731             minutes = '0' + minutes;
23732         }
23733         
23734         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23735         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23736         this.pop.select('button', true).first().dom.innerHTML = period;
23737         
23738     },
23739     
23740     place: function()
23741     {   
23742         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23743         
23744         var cls = ['bottom'];
23745         
23746         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23747             cls.pop();
23748             cls.push('top');
23749         }
23750         
23751         cls.push('right');
23752         
23753         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23754             cls.pop();
23755             cls.push('left');
23756         }
23757         //this.picker().setXY(20000,20000);
23758         this.picker().addClass(cls.join('-'));
23759         
23760         var _this = this;
23761         
23762         Roo.each(cls, function(c){
23763             if(c == 'bottom'){
23764                 (function() {
23765                  //  
23766                 }).defer(200);
23767                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23768                 //_this.picker().setTop(_this.inputEl().getHeight());
23769                 return;
23770             }
23771             if(c == 'top'){
23772                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23773                 
23774                 //_this.picker().setTop(0 - _this.picker().getHeight());
23775                 return;
23776             }
23777             /*
23778             if(c == 'left'){
23779                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23780                 return;
23781             }
23782             if(c == 'right'){
23783                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23784                 return;
23785             }
23786             */
23787         });
23788         
23789     },
23790   
23791     onFocus : function()
23792     {
23793         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23794         this.show();
23795     },
23796     
23797     onBlur : function()
23798     {
23799         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23800         this.hide();
23801     },
23802     
23803     show : function()
23804     {
23805         this.picker().show();
23806         this.pop.show();
23807         this.update();
23808         this.place();
23809         
23810         this.fireEvent('show', this, this.date);
23811     },
23812     
23813     hide : function()
23814     {
23815         this.picker().hide();
23816         this.pop.hide();
23817         
23818         this.fireEvent('hide', this, this.date);
23819     },
23820     
23821     setTime : function()
23822     {
23823         this.hide();
23824         this.setValue(this.time.format(this.format));
23825         
23826         this.fireEvent('select', this, this.date);
23827         
23828         
23829     },
23830     
23831     onMousedown: function(e){
23832         e.stopPropagation();
23833         e.preventDefault();
23834     },
23835     
23836     onIncrementHours: function()
23837     {
23838         Roo.log('onIncrementHours');
23839         this.time = this.time.add(Date.HOUR, 1);
23840         this.update();
23841         
23842     },
23843     
23844     onDecrementHours: function()
23845     {
23846         Roo.log('onDecrementHours');
23847         this.time = this.time.add(Date.HOUR, -1);
23848         this.update();
23849     },
23850     
23851     onIncrementMinutes: function()
23852     {
23853         Roo.log('onIncrementMinutes');
23854         this.time = this.time.add(Date.MINUTE, 1);
23855         this.update();
23856     },
23857     
23858     onDecrementMinutes: function()
23859     {
23860         Roo.log('onDecrementMinutes');
23861         this.time = this.time.add(Date.MINUTE, -1);
23862         this.update();
23863     },
23864     
23865     onTogglePeriod: function()
23866     {
23867         Roo.log('onTogglePeriod');
23868         this.time = this.time.add(Date.HOUR, 12);
23869         this.update();
23870     }
23871     
23872    
23873 });
23874  
23875
23876 Roo.apply(Roo.bootstrap.TimeField,  {
23877   
23878     template : {
23879         tag: 'div',
23880         cls: 'datepicker dropdown-menu',
23881         cn: [
23882             {
23883                 tag: 'div',
23884                 cls: 'datepicker-time',
23885                 cn: [
23886                 {
23887                     tag: 'table',
23888                     cls: 'table-condensed',
23889                     cn:[
23890                         {
23891                             tag: 'tbody',
23892                             cn: [
23893                                 {
23894                                     tag: 'tr',
23895                                     cn: [
23896                                     {
23897                                         tag: 'td',
23898                                         colspan: '7'
23899                                     }
23900                                     ]
23901                                 }
23902                             ]
23903                         },
23904                         {
23905                             tag: 'tfoot',
23906                             cn: [
23907                                 {
23908                                     tag: 'tr',
23909                                     cn: [
23910                                     {
23911                                         tag: 'th',
23912                                         colspan: '7',
23913                                         cls: '',
23914                                         cn: [
23915                                             {
23916                                                 tag: 'button',
23917                                                 cls: 'btn btn-info ok',
23918                                                 html: 'OK'
23919                                             }
23920                                         ]
23921                                     }
23922                     
23923                                     ]
23924                                 }
23925                             ]
23926                         }
23927                     ]
23928                 }
23929                 ]
23930             }
23931         ]
23932     }
23933 });
23934
23935  
23936
23937  /*
23938  * - LGPL
23939  *
23940  * MonthField
23941  * 
23942  */
23943
23944 /**
23945  * @class Roo.bootstrap.MonthField
23946  * @extends Roo.bootstrap.Input
23947  * Bootstrap MonthField class
23948  * 
23949  * @cfg {String} language default en
23950  * 
23951  * @constructor
23952  * Create a new MonthField
23953  * @param {Object} config The config object
23954  */
23955
23956 Roo.bootstrap.MonthField = function(config){
23957     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23958     
23959     this.addEvents({
23960         /**
23961          * @event show
23962          * Fires when this field show.
23963          * @param {Roo.bootstrap.MonthField} this
23964          * @param {Mixed} date The date value
23965          */
23966         show : true,
23967         /**
23968          * @event show
23969          * Fires when this field hide.
23970          * @param {Roo.bootstrap.MonthField} this
23971          * @param {Mixed} date The date value
23972          */
23973         hide : true,
23974         /**
23975          * @event select
23976          * Fires when select a date.
23977          * @param {Roo.bootstrap.MonthField} this
23978          * @param {String} oldvalue The old value
23979          * @param {String} newvalue The new value
23980          */
23981         select : true
23982     });
23983 };
23984
23985 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
23986     
23987     onRender: function(ct, position)
23988     {
23989         
23990         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23991         
23992         this.language = this.language || 'en';
23993         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23994         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23995         
23996         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23997         this.isInline = false;
23998         this.isInput = true;
23999         this.component = this.el.select('.add-on', true).first() || false;
24000         this.component = (this.component && this.component.length === 0) ? false : this.component;
24001         this.hasInput = this.component && this.inputEL().length;
24002         
24003         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24004         
24005         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24006         
24007         this.picker().on('mousedown', this.onMousedown, this);
24008         this.picker().on('click', this.onClick, this);
24009         
24010         this.picker().addClass('datepicker-dropdown');
24011         
24012         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24013             v.setStyle('width', '189px');
24014         });
24015         
24016         this.fillMonths();
24017         
24018         this.update();
24019         
24020         if(this.isInline) {
24021             this.show();
24022         }
24023         
24024     },
24025     
24026     setValue: function(v, suppressEvent)
24027     {   
24028         var o = this.getValue();
24029         
24030         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24031         
24032         this.update();
24033
24034         if(suppressEvent !== true){
24035             this.fireEvent('select', this, o, v);
24036         }
24037         
24038     },
24039     
24040     getValue: function()
24041     {
24042         return this.value;
24043     },
24044     
24045     onClick: function(e) 
24046     {
24047         e.stopPropagation();
24048         e.preventDefault();
24049         
24050         var target = e.getTarget();
24051         
24052         if(target.nodeName.toLowerCase() === 'i'){
24053             target = Roo.get(target).dom.parentNode;
24054         }
24055         
24056         var nodeName = target.nodeName;
24057         var className = target.className;
24058         var html = target.innerHTML;
24059         
24060         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24061             return;
24062         }
24063         
24064         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24065         
24066         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24067         
24068         this.hide();
24069                         
24070     },
24071     
24072     picker : function()
24073     {
24074         return this.pickerEl;
24075     },
24076     
24077     fillMonths: function()
24078     {    
24079         var i = 0;
24080         var months = this.picker().select('>.datepicker-months td', true).first();
24081         
24082         months.dom.innerHTML = '';
24083         
24084         while (i < 12) {
24085             var month = {
24086                 tag: 'span',
24087                 cls: 'month',
24088                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24089             };
24090             
24091             months.createChild(month);
24092         }
24093         
24094     },
24095     
24096     update: function()
24097     {
24098         var _this = this;
24099         
24100         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24101             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24102         }
24103         
24104         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24105             e.removeClass('active');
24106             
24107             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24108                 e.addClass('active');
24109             }
24110         })
24111     },
24112     
24113     place: function()
24114     {
24115         if(this.isInline) {
24116             return;
24117         }
24118         
24119         this.picker().removeClass(['bottom', 'top']);
24120         
24121         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24122             /*
24123              * place to the top of element!
24124              *
24125              */
24126             
24127             this.picker().addClass('top');
24128             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24129             
24130             return;
24131         }
24132         
24133         this.picker().addClass('bottom');
24134         
24135         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24136     },
24137     
24138     onFocus : function()
24139     {
24140         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24141         this.show();
24142     },
24143     
24144     onBlur : function()
24145     {
24146         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24147         
24148         var d = this.inputEl().getValue();
24149         
24150         this.setValue(d);
24151                 
24152         this.hide();
24153     },
24154     
24155     show : function()
24156     {
24157         this.picker().show();
24158         this.picker().select('>.datepicker-months', true).first().show();
24159         this.update();
24160         this.place();
24161         
24162         this.fireEvent('show', this, this.date);
24163     },
24164     
24165     hide : function()
24166     {
24167         if(this.isInline) {
24168             return;
24169         }
24170         this.picker().hide();
24171         this.fireEvent('hide', this, this.date);
24172         
24173     },
24174     
24175     onMousedown: function(e)
24176     {
24177         e.stopPropagation();
24178         e.preventDefault();
24179     },
24180     
24181     keyup: function(e)
24182     {
24183         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24184         this.update();
24185     },
24186
24187     fireKey: function(e)
24188     {
24189         if (!this.picker().isVisible()){
24190             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24191                 this.show();
24192             }
24193             return;
24194         }
24195         
24196         var dir;
24197         
24198         switch(e.keyCode){
24199             case 27: // escape
24200                 this.hide();
24201                 e.preventDefault();
24202                 break;
24203             case 37: // left
24204             case 39: // right
24205                 dir = e.keyCode == 37 ? -1 : 1;
24206                 
24207                 this.vIndex = this.vIndex + dir;
24208                 
24209                 if(this.vIndex < 0){
24210                     this.vIndex = 0;
24211                 }
24212                 
24213                 if(this.vIndex > 11){
24214                     this.vIndex = 11;
24215                 }
24216                 
24217                 if(isNaN(this.vIndex)){
24218                     this.vIndex = 0;
24219                 }
24220                 
24221                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24222                 
24223                 break;
24224             case 38: // up
24225             case 40: // down
24226                 
24227                 dir = e.keyCode == 38 ? -1 : 1;
24228                 
24229                 this.vIndex = this.vIndex + dir * 4;
24230                 
24231                 if(this.vIndex < 0){
24232                     this.vIndex = 0;
24233                 }
24234                 
24235                 if(this.vIndex > 11){
24236                     this.vIndex = 11;
24237                 }
24238                 
24239                 if(isNaN(this.vIndex)){
24240                     this.vIndex = 0;
24241                 }
24242                 
24243                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24244                 break;
24245                 
24246             case 13: // enter
24247                 
24248                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24249                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24250                 }
24251                 
24252                 this.hide();
24253                 e.preventDefault();
24254                 break;
24255             case 9: // tab
24256                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24257                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24258                 }
24259                 this.hide();
24260                 break;
24261             case 16: // shift
24262             case 17: // ctrl
24263             case 18: // alt
24264                 break;
24265             default :
24266                 this.hide();
24267                 
24268         }
24269     },
24270     
24271     remove: function() 
24272     {
24273         this.picker().remove();
24274     }
24275    
24276 });
24277
24278 Roo.apply(Roo.bootstrap.MonthField,  {
24279     
24280     content : {
24281         tag: 'tbody',
24282         cn: [
24283         {
24284             tag: 'tr',
24285             cn: [
24286             {
24287                 tag: 'td',
24288                 colspan: '7'
24289             }
24290             ]
24291         }
24292         ]
24293     },
24294     
24295     dates:{
24296         en: {
24297             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24298             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24299         }
24300     }
24301 });
24302
24303 Roo.apply(Roo.bootstrap.MonthField,  {
24304   
24305     template : {
24306         tag: 'div',
24307         cls: 'datepicker dropdown-menu roo-dynamic',
24308         cn: [
24309             {
24310                 tag: 'div',
24311                 cls: 'datepicker-months',
24312                 cn: [
24313                 {
24314                     tag: 'table',
24315                     cls: 'table-condensed',
24316                     cn:[
24317                         Roo.bootstrap.DateField.content
24318                     ]
24319                 }
24320                 ]
24321             }
24322         ]
24323     }
24324 });
24325
24326  
24327
24328  
24329  /*
24330  * - LGPL
24331  *
24332  * CheckBox
24333  * 
24334  */
24335
24336 /**
24337  * @class Roo.bootstrap.CheckBox
24338  * @extends Roo.bootstrap.Input
24339  * Bootstrap CheckBox class
24340  * 
24341  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24342  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24343  * @cfg {String} boxLabel The text that appears beside the checkbox
24344  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24345  * @cfg {Boolean} checked initnal the element
24346  * @cfg {Boolean} inline inline the element (default false)
24347  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24348  * @cfg {String} tooltip label tooltip
24349  * 
24350  * @constructor
24351  * Create a new CheckBox
24352  * @param {Object} config The config object
24353  */
24354
24355 Roo.bootstrap.CheckBox = function(config){
24356     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24357    
24358     this.addEvents({
24359         /**
24360         * @event check
24361         * Fires when the element is checked or unchecked.
24362         * @param {Roo.bootstrap.CheckBox} this This input
24363         * @param {Boolean} checked The new checked value
24364         */
24365        check : true,
24366        /**
24367         * @event click
24368         * Fires when the element is click.
24369         * @param {Roo.bootstrap.CheckBox} this This input
24370         */
24371        click : true
24372     });
24373     
24374 };
24375
24376 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24377   
24378     inputType: 'checkbox',
24379     inputValue: 1,
24380     valueOff: 0,
24381     boxLabel: false,
24382     checked: false,
24383     weight : false,
24384     inline: false,
24385     tooltip : '',
24386     
24387     // checkbox success does not make any sense really.. 
24388     invalidClass : "",
24389     validClass : "",
24390     
24391     
24392     getAutoCreate : function()
24393     {
24394         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24395         
24396         var id = Roo.id();
24397         
24398         var cfg = {};
24399         
24400         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24401         
24402         if(this.inline){
24403             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24404         }
24405         
24406         var input =  {
24407             tag: 'input',
24408             id : id,
24409             type : this.inputType,
24410             value : this.inputValue,
24411             cls : 'roo-' + this.inputType, //'form-box',
24412             placeholder : this.placeholder || ''
24413             
24414         };
24415         
24416         if(this.inputType != 'radio'){
24417             var hidden =  {
24418                 tag: 'input',
24419                 type : 'hidden',
24420                 cls : 'roo-hidden-value',
24421                 value : this.checked ? this.inputValue : this.valueOff
24422             };
24423         }
24424         
24425             
24426         if (this.weight) { // Validity check?
24427             cfg.cls += " " + this.inputType + "-" + this.weight;
24428         }
24429         
24430         if (this.disabled) {
24431             input.disabled=true;
24432         }
24433         
24434         if(this.checked){
24435             input.checked = this.checked;
24436         }
24437         
24438         if (this.name) {
24439             
24440             input.name = this.name;
24441             
24442             if(this.inputType != 'radio'){
24443                 hidden.name = this.name;
24444                 input.name = '_hidden_' + this.name;
24445             }
24446         }
24447         
24448         if (this.size) {
24449             input.cls += ' input-' + this.size;
24450         }
24451         
24452         var settings=this;
24453         
24454         ['xs','sm','md','lg'].map(function(size){
24455             if (settings[size]) {
24456                 cfg.cls += ' col-' + size + '-' + settings[size];
24457             }
24458         });
24459         
24460         var inputblock = input;
24461          
24462         if (this.before || this.after) {
24463             
24464             inputblock = {
24465                 cls : 'input-group',
24466                 cn :  [] 
24467             };
24468             
24469             if (this.before) {
24470                 inputblock.cn.push({
24471                     tag :'span',
24472                     cls : 'input-group-addon',
24473                     html : this.before
24474                 });
24475             }
24476             
24477             inputblock.cn.push(input);
24478             
24479             if(this.inputType != 'radio'){
24480                 inputblock.cn.push(hidden);
24481             }
24482             
24483             if (this.after) {
24484                 inputblock.cn.push({
24485                     tag :'span',
24486                     cls : 'input-group-addon',
24487                     html : this.after
24488                 });
24489             }
24490             
24491         }
24492         var boxLabelCfg = false;
24493         
24494         if(this.boxLabel){
24495            
24496             boxLabelCfg = {
24497                 tag: 'label',
24498                 //'for': id, // box label is handled by onclick - so no for...
24499                 cls: 'box-label',
24500                 html: this.boxLabel
24501             };
24502             if(this.tooltip){
24503                 boxLabelCfg.tooltip = this.tooltip;
24504             }
24505              
24506         }
24507         
24508         
24509         if (align ==='left' && this.fieldLabel.length) {
24510 //                Roo.log("left and has label");
24511             cfg.cn = [
24512                 {
24513                     tag: 'label',
24514                     'for' :  id,
24515                     cls : 'control-label',
24516                     html : this.fieldLabel
24517                 },
24518                 {
24519                     cls : "", 
24520                     cn: [
24521                         inputblock
24522                     ]
24523                 }
24524             ];
24525             
24526             if (boxLabelCfg) {
24527                 cfg.cn[1].cn.push(boxLabelCfg);
24528             }
24529             
24530             if(this.labelWidth > 12){
24531                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24532             }
24533             
24534             if(this.labelWidth < 13 && this.labelmd == 0){
24535                 this.labelmd = this.labelWidth;
24536             }
24537             
24538             if(this.labellg > 0){
24539                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24540                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24541             }
24542             
24543             if(this.labelmd > 0){
24544                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24545                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24546             }
24547             
24548             if(this.labelsm > 0){
24549                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24550                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24551             }
24552             
24553             if(this.labelxs > 0){
24554                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24555                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24556             }
24557             
24558         } else if ( this.fieldLabel.length) {
24559 //                Roo.log(" label");
24560                 cfg.cn = [
24561                    
24562                     {
24563                         tag: this.boxLabel ? 'span' : 'label',
24564                         'for': id,
24565                         cls: 'control-label box-input-label',
24566                         //cls : 'input-group-addon',
24567                         html : this.fieldLabel
24568                     },
24569                     
24570                     inputblock
24571                     
24572                 ];
24573                 if (boxLabelCfg) {
24574                     cfg.cn.push(boxLabelCfg);
24575                 }
24576
24577         } else {
24578             
24579 //                Roo.log(" no label && no align");
24580                 cfg.cn = [  inputblock ] ;
24581                 if (boxLabelCfg) {
24582                     cfg.cn.push(boxLabelCfg);
24583                 }
24584
24585                 
24586         }
24587         
24588        
24589         
24590         if(this.inputType != 'radio'){
24591             cfg.cn.push(hidden);
24592         }
24593         
24594         return cfg;
24595         
24596     },
24597     
24598     /**
24599      * return the real input element.
24600      */
24601     inputEl: function ()
24602     {
24603         return this.el.select('input.roo-' + this.inputType,true).first();
24604     },
24605     hiddenEl: function ()
24606     {
24607         return this.el.select('input.roo-hidden-value',true).first();
24608     },
24609     
24610     labelEl: function()
24611     {
24612         return this.el.select('label.control-label',true).first();
24613     },
24614     /* depricated... */
24615     
24616     label: function()
24617     {
24618         return this.labelEl();
24619     },
24620     
24621     boxLabelEl: function()
24622     {
24623         return this.el.select('label.box-label',true).first();
24624     },
24625     
24626     initEvents : function()
24627     {
24628 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24629         
24630         this.inputEl().on('click', this.onClick,  this);
24631         
24632         if (this.boxLabel) { 
24633             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24634         }
24635         
24636         this.startValue = this.getValue();
24637         
24638         if(this.groupId){
24639             Roo.bootstrap.CheckBox.register(this);
24640         }
24641     },
24642     
24643     onClick : function(e)
24644     {   
24645         if(this.fireEvent('click', this, e) !== false){
24646             this.setChecked(!this.checked);
24647         }
24648         
24649     },
24650     
24651     setChecked : function(state,suppressEvent)
24652     {
24653         this.startValue = this.getValue();
24654
24655         if(this.inputType == 'radio'){
24656             
24657             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24658                 e.dom.checked = false;
24659             });
24660             
24661             this.inputEl().dom.checked = true;
24662             
24663             this.inputEl().dom.value = this.inputValue;
24664             
24665             if(suppressEvent !== true){
24666                 this.fireEvent('check', this, true);
24667             }
24668             
24669             this.validate();
24670             
24671             return;
24672         }
24673         
24674         this.checked = state;
24675         
24676         this.inputEl().dom.checked = state;
24677         
24678         
24679         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24680         
24681         if(suppressEvent !== true){
24682             this.fireEvent('check', this, state);
24683         }
24684         
24685         this.validate();
24686     },
24687     
24688     getValue : function()
24689     {
24690         if(this.inputType == 'radio'){
24691             return this.getGroupValue();
24692         }
24693         
24694         return this.hiddenEl().dom.value;
24695         
24696     },
24697     
24698     getGroupValue : function()
24699     {
24700         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24701             return '';
24702         }
24703         
24704         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24705     },
24706     
24707     setValue : function(v,suppressEvent)
24708     {
24709         if(this.inputType == 'radio'){
24710             this.setGroupValue(v, suppressEvent);
24711             return;
24712         }
24713         
24714         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24715         
24716         this.validate();
24717     },
24718     
24719     setGroupValue : function(v, suppressEvent)
24720     {
24721         this.startValue = this.getValue();
24722         
24723         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24724             e.dom.checked = false;
24725             
24726             if(e.dom.value == v){
24727                 e.dom.checked = true;
24728             }
24729         });
24730         
24731         if(suppressEvent !== true){
24732             this.fireEvent('check', this, true);
24733         }
24734
24735         this.validate();
24736         
24737         return;
24738     },
24739     
24740     validate : function()
24741     {
24742         if(this.getVisibilityEl().hasClass('hidden')){
24743             return true;
24744         }
24745         
24746         if(
24747                 this.disabled || 
24748                 (this.inputType == 'radio' && this.validateRadio()) ||
24749                 (this.inputType == 'checkbox' && this.validateCheckbox())
24750         ){
24751             this.markValid();
24752             return true;
24753         }
24754         
24755         this.markInvalid();
24756         return false;
24757     },
24758     
24759     validateRadio : function()
24760     {
24761         if(this.getVisibilityEl().hasClass('hidden')){
24762             return true;
24763         }
24764         
24765         if(this.allowBlank){
24766             return true;
24767         }
24768         
24769         var valid = false;
24770         
24771         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24772             if(!e.dom.checked){
24773                 return;
24774             }
24775             
24776             valid = true;
24777             
24778             return false;
24779         });
24780         
24781         return valid;
24782     },
24783     
24784     validateCheckbox : function()
24785     {
24786         if(!this.groupId){
24787             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24788             //return (this.getValue() == this.inputValue) ? true : false;
24789         }
24790         
24791         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24792         
24793         if(!group){
24794             return false;
24795         }
24796         
24797         var r = false;
24798         
24799         for(var i in group){
24800             if(group[i].el.isVisible(true)){
24801                 r = false;
24802                 break;
24803             }
24804             
24805             r = true;
24806         }
24807         
24808         for(var i in group){
24809             if(r){
24810                 break;
24811             }
24812             
24813             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24814         }
24815         
24816         return r;
24817     },
24818     
24819     /**
24820      * Mark this field as valid
24821      */
24822     markValid : function()
24823     {
24824         var _this = this;
24825         
24826         this.fireEvent('valid', this);
24827         
24828         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24829         
24830         if(this.groupId){
24831             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24832         }
24833         
24834         if(label){
24835             label.markValid();
24836         }
24837
24838         if(this.inputType == 'radio'){
24839             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24840                 var fg = e.findParent('.form-group', false, true);
24841                 if (Roo.bootstrap.version == 3) {
24842                     fg.removeClass([_this.invalidClass, _this.validClass]);
24843                     fg.addClass(_this.validClass);
24844                 } else {
24845                     fg.removeClass(['is-valid', 'is-invalid']);
24846                     fg.addClass('is-valid');
24847                 }
24848             });
24849             
24850             return;
24851         }
24852
24853         if(!this.groupId){
24854             var fg = this.el.findParent('.form-group', false, true);
24855             if (Roo.bootstrap.version == 3) {
24856                 fg.removeClass([this.invalidClass, this.validClass]);
24857                 fg.addClass(this.validClass);
24858             } else {
24859                 fg.removeClass(['is-valid', 'is-invalid']);
24860                 fg.addClass('is-valid');
24861             }
24862             return;
24863         }
24864         
24865         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24866         
24867         if(!group){
24868             return;
24869         }
24870         
24871         for(var i in group){
24872             var fg = group[i].el.findParent('.form-group', false, true);
24873             if (Roo.bootstrap.version == 3) {
24874                 fg.removeClass([this.invalidClass, this.validClass]);
24875                 fg.addClass(this.validClass);
24876             } else {
24877                 fg.removeClass(['is-valid', 'is-invalid']);
24878                 fg.addClass('is-valid');
24879             }
24880         }
24881     },
24882     
24883      /**
24884      * Mark this field as invalid
24885      * @param {String} msg The validation message
24886      */
24887     markInvalid : function(msg)
24888     {
24889         if(this.allowBlank){
24890             return;
24891         }
24892         
24893         var _this = this;
24894         
24895         this.fireEvent('invalid', this, msg);
24896         
24897         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24898         
24899         if(this.groupId){
24900             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24901         }
24902         
24903         if(label){
24904             label.markInvalid();
24905         }
24906             
24907         if(this.inputType == 'radio'){
24908             
24909             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24910                 var fg = e.findParent('.form-group', false, true);
24911                 if (Roo.bootstrap.version == 3) {
24912                     fg.removeClass([_this.invalidClass, _this.validClass]);
24913                     fg.addClass(_this.invalidClass);
24914                 } else {
24915                     fg.removeClass(['is-invalid', 'is-valid']);
24916                     fg.addClass('is-invalid');
24917                 }
24918             });
24919             
24920             return;
24921         }
24922         
24923         if(!this.groupId){
24924             var fg = this.el.findParent('.form-group', false, true);
24925             if (Roo.bootstrap.version == 3) {
24926                 fg.removeClass([_this.invalidClass, _this.validClass]);
24927                 fg.addClass(_this.invalidClass);
24928             } else {
24929                 fg.removeClass(['is-invalid', 'is-valid']);
24930                 fg.addClass('is-invalid');
24931             }
24932             return;
24933         }
24934         
24935         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24936         
24937         if(!group){
24938             return;
24939         }
24940         
24941         for(var i in group){
24942             var fg = group[i].el.findParent('.form-group', false, true);
24943             if (Roo.bootstrap.version == 3) {
24944                 fg.removeClass([_this.invalidClass, _this.validClass]);
24945                 fg.addClass(_this.invalidClass);
24946             } else {
24947                 fg.removeClass(['is-invalid', 'is-valid']);
24948                 fg.addClass('is-invalid');
24949             }
24950         }
24951         
24952     },
24953     
24954     clearInvalid : function()
24955     {
24956         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24957         
24958         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24959         
24960         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24961         
24962         if (label && label.iconEl) {
24963             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24964             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24965         }
24966     },
24967     
24968     disable : function()
24969     {
24970         if(this.inputType != 'radio'){
24971             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24972             return;
24973         }
24974         
24975         var _this = this;
24976         
24977         if(this.rendered){
24978             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24979                 _this.getActionEl().addClass(this.disabledClass);
24980                 e.dom.disabled = true;
24981             });
24982         }
24983         
24984         this.disabled = true;
24985         this.fireEvent("disable", this);
24986         return this;
24987     },
24988
24989     enable : function()
24990     {
24991         if(this.inputType != 'radio'){
24992             Roo.bootstrap.CheckBox.superclass.enable.call(this);
24993             return;
24994         }
24995         
24996         var _this = this;
24997         
24998         if(this.rendered){
24999             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25000                 _this.getActionEl().removeClass(this.disabledClass);
25001                 e.dom.disabled = false;
25002             });
25003         }
25004         
25005         this.disabled = false;
25006         this.fireEvent("enable", this);
25007         return this;
25008     },
25009     
25010     setBoxLabel : function(v)
25011     {
25012         this.boxLabel = v;
25013         
25014         if(this.rendered){
25015             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25016         }
25017     }
25018
25019 });
25020
25021 Roo.apply(Roo.bootstrap.CheckBox, {
25022     
25023     groups: {},
25024     
25025      /**
25026     * register a CheckBox Group
25027     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25028     */
25029     register : function(checkbox)
25030     {
25031         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25032             this.groups[checkbox.groupId] = {};
25033         }
25034         
25035         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25036             return;
25037         }
25038         
25039         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25040         
25041     },
25042     /**
25043     * fetch a CheckBox Group based on the group ID
25044     * @param {string} the group ID
25045     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25046     */
25047     get: function(groupId) {
25048         if (typeof(this.groups[groupId]) == 'undefined') {
25049             return false;
25050         }
25051         
25052         return this.groups[groupId] ;
25053     }
25054     
25055     
25056 });
25057 /*
25058  * - LGPL
25059  *
25060  * RadioItem
25061  * 
25062  */
25063
25064 /**
25065  * @class Roo.bootstrap.Radio
25066  * @extends Roo.bootstrap.Component
25067  * Bootstrap Radio class
25068  * @cfg {String} boxLabel - the label associated
25069  * @cfg {String} value - the value of radio
25070  * 
25071  * @constructor
25072  * Create a new Radio
25073  * @param {Object} config The config object
25074  */
25075 Roo.bootstrap.Radio = function(config){
25076     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25077     
25078 };
25079
25080 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25081     
25082     boxLabel : '',
25083     
25084     value : '',
25085     
25086     getAutoCreate : function()
25087     {
25088         var cfg = {
25089             tag : 'div',
25090             cls : 'form-group radio',
25091             cn : [
25092                 {
25093                     tag : 'label',
25094                     cls : 'box-label',
25095                     html : this.boxLabel
25096                 }
25097             ]
25098         };
25099         
25100         return cfg;
25101     },
25102     
25103     initEvents : function() 
25104     {
25105         this.parent().register(this);
25106         
25107         this.el.on('click', this.onClick, this);
25108         
25109     },
25110     
25111     onClick : function(e)
25112     {
25113         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25114             this.setChecked(true);
25115         }
25116     },
25117     
25118     setChecked : function(state, suppressEvent)
25119     {
25120         this.parent().setValue(this.value, suppressEvent);
25121         
25122     },
25123     
25124     setBoxLabel : function(v)
25125     {
25126         this.boxLabel = v;
25127         
25128         if(this.rendered){
25129             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25130         }
25131     }
25132     
25133 });
25134  
25135
25136  /*
25137  * - LGPL
25138  *
25139  * Input
25140  * 
25141  */
25142
25143 /**
25144  * @class Roo.bootstrap.SecurePass
25145  * @extends Roo.bootstrap.Input
25146  * Bootstrap SecurePass class
25147  *
25148  * 
25149  * @constructor
25150  * Create a new SecurePass
25151  * @param {Object} config The config object
25152  */
25153  
25154 Roo.bootstrap.SecurePass = function (config) {
25155     // these go here, so the translation tool can replace them..
25156     this.errors = {
25157         PwdEmpty: "Please type a password, and then retype it to confirm.",
25158         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25159         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25160         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25161         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25162         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25163         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25164         TooWeak: "Your password is Too Weak."
25165     },
25166     this.meterLabel = "Password strength:";
25167     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25168     this.meterClass = [
25169         "roo-password-meter-tooweak", 
25170         "roo-password-meter-weak", 
25171         "roo-password-meter-medium", 
25172         "roo-password-meter-strong", 
25173         "roo-password-meter-grey"
25174     ];
25175     
25176     this.errors = {};
25177     
25178     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25179 }
25180
25181 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25182     /**
25183      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25184      * {
25185      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25186      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25187      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25188      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25189      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25190      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25191      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25192      * })
25193      */
25194     // private
25195     
25196     meterWidth: 300,
25197     errorMsg :'',    
25198     errors: false,
25199     imageRoot: '/',
25200     /**
25201      * @cfg {String/Object} Label for the strength meter (defaults to
25202      * 'Password strength:')
25203      */
25204     // private
25205     meterLabel: '',
25206     /**
25207      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25208      * ['Weak', 'Medium', 'Strong'])
25209      */
25210     // private    
25211     pwdStrengths: false,    
25212     // private
25213     strength: 0,
25214     // private
25215     _lastPwd: null,
25216     // private
25217     kCapitalLetter: 0,
25218     kSmallLetter: 1,
25219     kDigit: 2,
25220     kPunctuation: 3,
25221     
25222     insecure: false,
25223     // private
25224     initEvents: function ()
25225     {
25226         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25227
25228         if (this.el.is('input[type=password]') && Roo.isSafari) {
25229             this.el.on('keydown', this.SafariOnKeyDown, this);
25230         }
25231
25232         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25233     },
25234     // private
25235     onRender: function (ct, position)
25236     {
25237         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25238         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25239         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25240
25241         this.trigger.createChild({
25242                    cn: [
25243                     {
25244                     //id: 'PwdMeter',
25245                     tag: 'div',
25246                     cls: 'roo-password-meter-grey col-xs-12',
25247                     style: {
25248                         //width: 0,
25249                         //width: this.meterWidth + 'px'                                                
25250                         }
25251                     },
25252                     {                            
25253                          cls: 'roo-password-meter-text'                          
25254                     }
25255                 ]            
25256         });
25257
25258          
25259         if (this.hideTrigger) {
25260             this.trigger.setDisplayed(false);
25261         }
25262         this.setSize(this.width || '', this.height || '');
25263     },
25264     // private
25265     onDestroy: function ()
25266     {
25267         if (this.trigger) {
25268             this.trigger.removeAllListeners();
25269             this.trigger.remove();
25270         }
25271         if (this.wrap) {
25272             this.wrap.remove();
25273         }
25274         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25275     },
25276     // private
25277     checkStrength: function ()
25278     {
25279         var pwd = this.inputEl().getValue();
25280         if (pwd == this._lastPwd) {
25281             return;
25282         }
25283
25284         var strength;
25285         if (this.ClientSideStrongPassword(pwd)) {
25286             strength = 3;
25287         } else if (this.ClientSideMediumPassword(pwd)) {
25288             strength = 2;
25289         } else if (this.ClientSideWeakPassword(pwd)) {
25290             strength = 1;
25291         } else {
25292             strength = 0;
25293         }
25294         
25295         Roo.log('strength1: ' + strength);
25296         
25297         //var pm = this.trigger.child('div/div/div').dom;
25298         var pm = this.trigger.child('div/div');
25299         pm.removeClass(this.meterClass);
25300         pm.addClass(this.meterClass[strength]);
25301                 
25302         
25303         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25304                 
25305         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25306         
25307         this._lastPwd = pwd;
25308     },
25309     reset: function ()
25310     {
25311         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25312         
25313         this._lastPwd = '';
25314         
25315         var pm = this.trigger.child('div/div');
25316         pm.removeClass(this.meterClass);
25317         pm.addClass('roo-password-meter-grey');        
25318         
25319         
25320         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25321         
25322         pt.innerHTML = '';
25323         this.inputEl().dom.type='password';
25324     },
25325     // private
25326     validateValue: function (value)
25327     {
25328         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25329             return false;
25330         }
25331         if (value.length == 0) {
25332             if (this.allowBlank) {
25333                 this.clearInvalid();
25334                 return true;
25335             }
25336
25337             this.markInvalid(this.errors.PwdEmpty);
25338             this.errorMsg = this.errors.PwdEmpty;
25339             return false;
25340         }
25341         
25342         if(this.insecure){
25343             return true;
25344         }
25345         
25346         if (!value.match(/[\x21-\x7e]+/)) {
25347             this.markInvalid(this.errors.PwdBadChar);
25348             this.errorMsg = this.errors.PwdBadChar;
25349             return false;
25350         }
25351         if (value.length < 6) {
25352             this.markInvalid(this.errors.PwdShort);
25353             this.errorMsg = this.errors.PwdShort;
25354             return false;
25355         }
25356         if (value.length > 16) {
25357             this.markInvalid(this.errors.PwdLong);
25358             this.errorMsg = this.errors.PwdLong;
25359             return false;
25360         }
25361         var strength;
25362         if (this.ClientSideStrongPassword(value)) {
25363             strength = 3;
25364         } else if (this.ClientSideMediumPassword(value)) {
25365             strength = 2;
25366         } else if (this.ClientSideWeakPassword(value)) {
25367             strength = 1;
25368         } else {
25369             strength = 0;
25370         }
25371
25372         
25373         if (strength < 2) {
25374             //this.markInvalid(this.errors.TooWeak);
25375             this.errorMsg = this.errors.TooWeak;
25376             //return false;
25377         }
25378         
25379         
25380         console.log('strength2: ' + strength);
25381         
25382         //var pm = this.trigger.child('div/div/div').dom;
25383         
25384         var pm = this.trigger.child('div/div');
25385         pm.removeClass(this.meterClass);
25386         pm.addClass(this.meterClass[strength]);
25387                 
25388         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25389                 
25390         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25391         
25392         this.errorMsg = ''; 
25393         return true;
25394     },
25395     // private
25396     CharacterSetChecks: function (type)
25397     {
25398         this.type = type;
25399         this.fResult = false;
25400     },
25401     // private
25402     isctype: function (character, type)
25403     {
25404         switch (type) {  
25405             case this.kCapitalLetter:
25406                 if (character >= 'A' && character <= 'Z') {
25407                     return true;
25408                 }
25409                 break;
25410             
25411             case this.kSmallLetter:
25412                 if (character >= 'a' && character <= 'z') {
25413                     return true;
25414                 }
25415                 break;
25416             
25417             case this.kDigit:
25418                 if (character >= '0' && character <= '9') {
25419                     return true;
25420                 }
25421                 break;
25422             
25423             case this.kPunctuation:
25424                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25425                     return true;
25426                 }
25427                 break;
25428             
25429             default:
25430                 return false;
25431         }
25432
25433     },
25434     // private
25435     IsLongEnough: function (pwd, size)
25436     {
25437         return !(pwd == null || isNaN(size) || pwd.length < size);
25438     },
25439     // private
25440     SpansEnoughCharacterSets: function (word, nb)
25441     {
25442         if (!this.IsLongEnough(word, nb))
25443         {
25444             return false;
25445         }
25446
25447         var characterSetChecks = new Array(
25448             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25449             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25450         );
25451         
25452         for (var index = 0; index < word.length; ++index) {
25453             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25454                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25455                     characterSetChecks[nCharSet].fResult = true;
25456                     break;
25457                 }
25458             }
25459         }
25460
25461         var nCharSets = 0;
25462         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25463             if (characterSetChecks[nCharSet].fResult) {
25464                 ++nCharSets;
25465             }
25466         }
25467
25468         if (nCharSets < nb) {
25469             return false;
25470         }
25471         return true;
25472     },
25473     // private
25474     ClientSideStrongPassword: function (pwd)
25475     {
25476         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25477     },
25478     // private
25479     ClientSideMediumPassword: function (pwd)
25480     {
25481         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25482     },
25483     // private
25484     ClientSideWeakPassword: function (pwd)
25485     {
25486         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25487     }
25488           
25489 })//<script type="text/javascript">
25490
25491 /*
25492  * Based  Ext JS Library 1.1.1
25493  * Copyright(c) 2006-2007, Ext JS, LLC.
25494  * LGPL
25495  *
25496  */
25497  
25498 /**
25499  * @class Roo.HtmlEditorCore
25500  * @extends Roo.Component
25501  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25502  *
25503  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25504  */
25505
25506 Roo.HtmlEditorCore = function(config){
25507     
25508     
25509     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25510     
25511     
25512     this.addEvents({
25513         /**
25514          * @event initialize
25515          * Fires when the editor is fully initialized (including the iframe)
25516          * @param {Roo.HtmlEditorCore} this
25517          */
25518         initialize: true,
25519         /**
25520          * @event activate
25521          * Fires when the editor is first receives the focus. Any insertion must wait
25522          * until after this event.
25523          * @param {Roo.HtmlEditorCore} this
25524          */
25525         activate: true,
25526          /**
25527          * @event beforesync
25528          * Fires before the textarea is updated with content from the editor iframe. Return false
25529          * to cancel the sync.
25530          * @param {Roo.HtmlEditorCore} this
25531          * @param {String} html
25532          */
25533         beforesync: true,
25534          /**
25535          * @event beforepush
25536          * Fires before the iframe editor is updated with content from the textarea. Return false
25537          * to cancel the push.
25538          * @param {Roo.HtmlEditorCore} this
25539          * @param {String} html
25540          */
25541         beforepush: true,
25542          /**
25543          * @event sync
25544          * Fires when the textarea is updated with content from the editor iframe.
25545          * @param {Roo.HtmlEditorCore} this
25546          * @param {String} html
25547          */
25548         sync: true,
25549          /**
25550          * @event push
25551          * Fires when the iframe editor is updated with content from the textarea.
25552          * @param {Roo.HtmlEditorCore} this
25553          * @param {String} html
25554          */
25555         push: true,
25556         
25557         /**
25558          * @event editorevent
25559          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25560          * @param {Roo.HtmlEditorCore} this
25561          */
25562         editorevent: true
25563         
25564     });
25565     
25566     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25567     
25568     // defaults : white / black...
25569     this.applyBlacklists();
25570     
25571     
25572     
25573 };
25574
25575
25576 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25577
25578
25579      /**
25580      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25581      */
25582     
25583     owner : false,
25584     
25585      /**
25586      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25587      *                        Roo.resizable.
25588      */
25589     resizable : false,
25590      /**
25591      * @cfg {Number} height (in pixels)
25592      */   
25593     height: 300,
25594    /**
25595      * @cfg {Number} width (in pixels)
25596      */   
25597     width: 500,
25598     
25599     /**
25600      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25601      * 
25602      */
25603     stylesheets: false,
25604     
25605     // id of frame..
25606     frameId: false,
25607     
25608     // private properties
25609     validationEvent : false,
25610     deferHeight: true,
25611     initialized : false,
25612     activated : false,
25613     sourceEditMode : false,
25614     onFocus : Roo.emptyFn,
25615     iframePad:3,
25616     hideMode:'offsets',
25617     
25618     clearUp: true,
25619     
25620     // blacklist + whitelisted elements..
25621     black: false,
25622     white: false,
25623      
25624     bodyCls : '',
25625
25626     /**
25627      * Protected method that will not generally be called directly. It
25628      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25629      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25630      */
25631     getDocMarkup : function(){
25632         // body styles..
25633         var st = '';
25634         
25635         // inherit styels from page...?? 
25636         if (this.stylesheets === false) {
25637             
25638             Roo.get(document.head).select('style').each(function(node) {
25639                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25640             });
25641             
25642             Roo.get(document.head).select('link').each(function(node) { 
25643                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25644             });
25645             
25646         } else if (!this.stylesheets.length) {
25647                 // simple..
25648                 st = '<style type="text/css">' +
25649                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25650                    '</style>';
25651         } else {
25652             for (var i in this.stylesheets) { 
25653                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25654             }
25655             
25656         }
25657         
25658         st +=  '<style type="text/css">' +
25659             'IMG { cursor: pointer } ' +
25660         '</style>';
25661
25662         var cls = 'roo-htmleditor-body';
25663         
25664         if(this.bodyCls.length){
25665             cls += ' ' + this.bodyCls;
25666         }
25667         
25668         return '<html><head>' + st  +
25669             //<style type="text/css">' +
25670             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25671             //'</style>' +
25672             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25673     },
25674
25675     // private
25676     onRender : function(ct, position)
25677     {
25678         var _t = this;
25679         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25680         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25681         
25682         
25683         this.el.dom.style.border = '0 none';
25684         this.el.dom.setAttribute('tabIndex', -1);
25685         this.el.addClass('x-hidden hide');
25686         
25687         
25688         
25689         if(Roo.isIE){ // fix IE 1px bogus margin
25690             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25691         }
25692        
25693         
25694         this.frameId = Roo.id();
25695         
25696          
25697         
25698         var iframe = this.owner.wrap.createChild({
25699             tag: 'iframe',
25700             cls: 'form-control', // bootstrap..
25701             id: this.frameId,
25702             name: this.frameId,
25703             frameBorder : 'no',
25704             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25705         }, this.el
25706         );
25707         
25708         
25709         this.iframe = iframe.dom;
25710
25711          this.assignDocWin();
25712         
25713         this.doc.designMode = 'on';
25714        
25715         this.doc.open();
25716         this.doc.write(this.getDocMarkup());
25717         this.doc.close();
25718
25719         
25720         var task = { // must defer to wait for browser to be ready
25721             run : function(){
25722                 //console.log("run task?" + this.doc.readyState);
25723                 this.assignDocWin();
25724                 if(this.doc.body || this.doc.readyState == 'complete'){
25725                     try {
25726                         this.doc.designMode="on";
25727                     } catch (e) {
25728                         return;
25729                     }
25730                     Roo.TaskMgr.stop(task);
25731                     this.initEditor.defer(10, this);
25732                 }
25733             },
25734             interval : 10,
25735             duration: 10000,
25736             scope: this
25737         };
25738         Roo.TaskMgr.start(task);
25739
25740     },
25741
25742     // private
25743     onResize : function(w, h)
25744     {
25745          Roo.log('resize: ' +w + ',' + h );
25746         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25747         if(!this.iframe){
25748             return;
25749         }
25750         if(typeof w == 'number'){
25751             
25752             this.iframe.style.width = w + 'px';
25753         }
25754         if(typeof h == 'number'){
25755             
25756             this.iframe.style.height = h + 'px';
25757             if(this.doc){
25758                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25759             }
25760         }
25761         
25762     },
25763
25764     /**
25765      * Toggles the editor between standard and source edit mode.
25766      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25767      */
25768     toggleSourceEdit : function(sourceEditMode){
25769         
25770         this.sourceEditMode = sourceEditMode === true;
25771         
25772         if(this.sourceEditMode){
25773  
25774             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25775             
25776         }else{
25777             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25778             //this.iframe.className = '';
25779             this.deferFocus();
25780         }
25781         //this.setSize(this.owner.wrap.getSize());
25782         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25783     },
25784
25785     
25786   
25787
25788     /**
25789      * Protected method that will not generally be called directly. If you need/want
25790      * custom HTML cleanup, this is the method you should override.
25791      * @param {String} html The HTML to be cleaned
25792      * return {String} The cleaned HTML
25793      */
25794     cleanHtml : function(html){
25795         html = String(html);
25796         if(html.length > 5){
25797             if(Roo.isSafari){ // strip safari nonsense
25798                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25799             }
25800         }
25801         if(html == '&nbsp;'){
25802             html = '';
25803         }
25804         return html;
25805     },
25806
25807     /**
25808      * HTML Editor -> Textarea
25809      * Protected method that will not generally be called directly. Syncs the contents
25810      * of the editor iframe with the textarea.
25811      */
25812     syncValue : function(){
25813         if(this.initialized){
25814             var bd = (this.doc.body || this.doc.documentElement);
25815             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25816             var html = bd.innerHTML;
25817             if(Roo.isSafari){
25818                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25819                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25820                 if(m && m[1]){
25821                     html = '<div style="'+m[0]+'">' + html + '</div>';
25822                 }
25823             }
25824             html = this.cleanHtml(html);
25825             // fix up the special chars.. normaly like back quotes in word...
25826             // however we do not want to do this with chinese..
25827             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25828                 
25829                 var cc = match.charCodeAt();
25830
25831                 // Get the character value, handling surrogate pairs
25832                 if (match.length == 2) {
25833                     // It's a surrogate pair, calculate the Unicode code point
25834                     var high = match.charCodeAt(0) - 0xD800;
25835                     var low  = match.charCodeAt(1) - 0xDC00;
25836                     cc = (high * 0x400) + low + 0x10000;
25837                 }  else if (
25838                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25839                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25840                     (cc >= 0xf900 && cc < 0xfb00 )
25841                 ) {
25842                         return match;
25843                 }  
25844          
25845                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25846                 return "&#" + cc + ";";
25847                 
25848                 
25849             });
25850             
25851             
25852              
25853             if(this.owner.fireEvent('beforesync', this, html) !== false){
25854                 this.el.dom.value = html;
25855                 this.owner.fireEvent('sync', this, html);
25856             }
25857         }
25858     },
25859
25860     /**
25861      * Protected method that will not generally be called directly. Pushes the value of the textarea
25862      * into the iframe editor.
25863      */
25864     pushValue : function(){
25865         if(this.initialized){
25866             var v = this.el.dom.value.trim();
25867             
25868 //            if(v.length < 1){
25869 //                v = '&#160;';
25870 //            }
25871             
25872             if(this.owner.fireEvent('beforepush', this, v) !== false){
25873                 var d = (this.doc.body || this.doc.documentElement);
25874                 d.innerHTML = v;
25875                 this.cleanUpPaste();
25876                 this.el.dom.value = d.innerHTML;
25877                 this.owner.fireEvent('push', this, v);
25878             }
25879         }
25880     },
25881
25882     // private
25883     deferFocus : function(){
25884         this.focus.defer(10, this);
25885     },
25886
25887     // doc'ed in Field
25888     focus : function(){
25889         if(this.win && !this.sourceEditMode){
25890             this.win.focus();
25891         }else{
25892             this.el.focus();
25893         }
25894     },
25895     
25896     assignDocWin: function()
25897     {
25898         var iframe = this.iframe;
25899         
25900          if(Roo.isIE){
25901             this.doc = iframe.contentWindow.document;
25902             this.win = iframe.contentWindow;
25903         } else {
25904 //            if (!Roo.get(this.frameId)) {
25905 //                return;
25906 //            }
25907 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25908 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25909             
25910             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25911                 return;
25912             }
25913             
25914             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25915             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25916         }
25917     },
25918     
25919     // private
25920     initEditor : function(){
25921         //console.log("INIT EDITOR");
25922         this.assignDocWin();
25923         
25924         
25925         
25926         this.doc.designMode="on";
25927         this.doc.open();
25928         this.doc.write(this.getDocMarkup());
25929         this.doc.close();
25930         
25931         var dbody = (this.doc.body || this.doc.documentElement);
25932         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25933         // this copies styles from the containing element into thsi one..
25934         // not sure why we need all of this..
25935         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25936         
25937         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25938         //ss['background-attachment'] = 'fixed'; // w3c
25939         dbody.bgProperties = 'fixed'; // ie
25940         //Roo.DomHelper.applyStyles(dbody, ss);
25941         Roo.EventManager.on(this.doc, {
25942             //'mousedown': this.onEditorEvent,
25943             'mouseup': this.onEditorEvent,
25944             'dblclick': this.onEditorEvent,
25945             'click': this.onEditorEvent,
25946             'keyup': this.onEditorEvent,
25947             buffer:100,
25948             scope: this
25949         });
25950         if(Roo.isGecko){
25951             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25952         }
25953         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25954             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25955         }
25956         this.initialized = true;
25957
25958         this.owner.fireEvent('initialize', this);
25959         this.pushValue();
25960     },
25961
25962     // private
25963     onDestroy : function(){
25964         
25965         
25966         
25967         if(this.rendered){
25968             
25969             //for (var i =0; i < this.toolbars.length;i++) {
25970             //    // fixme - ask toolbars for heights?
25971             //    this.toolbars[i].onDestroy();
25972            // }
25973             
25974             //this.wrap.dom.innerHTML = '';
25975             //this.wrap.remove();
25976         }
25977     },
25978
25979     // private
25980     onFirstFocus : function(){
25981         
25982         this.assignDocWin();
25983         
25984         
25985         this.activated = true;
25986          
25987     
25988         if(Roo.isGecko){ // prevent silly gecko errors
25989             this.win.focus();
25990             var s = this.win.getSelection();
25991             if(!s.focusNode || s.focusNode.nodeType != 3){
25992                 var r = s.getRangeAt(0);
25993                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25994                 r.collapse(true);
25995                 this.deferFocus();
25996             }
25997             try{
25998                 this.execCmd('useCSS', true);
25999                 this.execCmd('styleWithCSS', false);
26000             }catch(e){}
26001         }
26002         this.owner.fireEvent('activate', this);
26003     },
26004
26005     // private
26006     adjustFont: function(btn){
26007         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26008         //if(Roo.isSafari){ // safari
26009         //    adjust *= 2;
26010        // }
26011         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26012         if(Roo.isSafari){ // safari
26013             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26014             v =  (v < 10) ? 10 : v;
26015             v =  (v > 48) ? 48 : v;
26016             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26017             
26018         }
26019         
26020         
26021         v = Math.max(1, v+adjust);
26022         
26023         this.execCmd('FontSize', v  );
26024     },
26025
26026     onEditorEvent : function(e)
26027     {
26028         this.owner.fireEvent('editorevent', this, e);
26029       //  this.updateToolbar();
26030         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26031     },
26032
26033     insertTag : function(tg)
26034     {
26035         // could be a bit smarter... -> wrap the current selected tRoo..
26036         if (tg.toLowerCase() == 'span' ||
26037             tg.toLowerCase() == 'code' ||
26038             tg.toLowerCase() == 'sup' ||
26039             tg.toLowerCase() == 'sub' 
26040             ) {
26041             
26042             range = this.createRange(this.getSelection());
26043             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26044             wrappingNode.appendChild(range.extractContents());
26045             range.insertNode(wrappingNode);
26046
26047             return;
26048             
26049             
26050             
26051         }
26052         this.execCmd("formatblock",   tg);
26053         
26054     },
26055     
26056     insertText : function(txt)
26057     {
26058         
26059         
26060         var range = this.createRange();
26061         range.deleteContents();
26062                //alert(Sender.getAttribute('label'));
26063                
26064         range.insertNode(this.doc.createTextNode(txt));
26065     } ,
26066     
26067      
26068
26069     /**
26070      * Executes a Midas editor command on the editor document and performs necessary focus and
26071      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26072      * @param {String} cmd The Midas command
26073      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26074      */
26075     relayCmd : function(cmd, value){
26076         this.win.focus();
26077         this.execCmd(cmd, value);
26078         this.owner.fireEvent('editorevent', this);
26079         //this.updateToolbar();
26080         this.owner.deferFocus();
26081     },
26082
26083     /**
26084      * Executes a Midas editor command directly on the editor document.
26085      * For visual commands, you should use {@link #relayCmd} instead.
26086      * <b>This should only be called after the editor is initialized.</b>
26087      * @param {String} cmd The Midas command
26088      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26089      */
26090     execCmd : function(cmd, value){
26091         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26092         this.syncValue();
26093     },
26094  
26095  
26096    
26097     /**
26098      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26099      * to insert tRoo.
26100      * @param {String} text | dom node.. 
26101      */
26102     insertAtCursor : function(text)
26103     {
26104         
26105         if(!this.activated){
26106             return;
26107         }
26108         /*
26109         if(Roo.isIE){
26110             this.win.focus();
26111             var r = this.doc.selection.createRange();
26112             if(r){
26113                 r.collapse(true);
26114                 r.pasteHTML(text);
26115                 this.syncValue();
26116                 this.deferFocus();
26117             
26118             }
26119             return;
26120         }
26121         */
26122         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26123             this.win.focus();
26124             
26125             
26126             // from jquery ui (MIT licenced)
26127             var range, node;
26128             var win = this.win;
26129             
26130             if (win.getSelection && win.getSelection().getRangeAt) {
26131                 range = win.getSelection().getRangeAt(0);
26132                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26133                 range.insertNode(node);
26134             } else if (win.document.selection && win.document.selection.createRange) {
26135                 // no firefox support
26136                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26137                 win.document.selection.createRange().pasteHTML(txt);
26138             } else {
26139                 // no firefox support
26140                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26141                 this.execCmd('InsertHTML', txt);
26142             } 
26143             
26144             this.syncValue();
26145             
26146             this.deferFocus();
26147         }
26148     },
26149  // private
26150     mozKeyPress : function(e){
26151         if(e.ctrlKey){
26152             var c = e.getCharCode(), cmd;
26153           
26154             if(c > 0){
26155                 c = String.fromCharCode(c).toLowerCase();
26156                 switch(c){
26157                     case 'b':
26158                         cmd = 'bold';
26159                         break;
26160                     case 'i':
26161                         cmd = 'italic';
26162                         break;
26163                     
26164                     case 'u':
26165                         cmd = 'underline';
26166                         break;
26167                     
26168                     case 'v':
26169                         this.cleanUpPaste.defer(100, this);
26170                         return;
26171                         
26172                 }
26173                 if(cmd){
26174                     this.win.focus();
26175                     this.execCmd(cmd);
26176                     this.deferFocus();
26177                     e.preventDefault();
26178                 }
26179                 
26180             }
26181         }
26182     },
26183
26184     // private
26185     fixKeys : function(){ // load time branching for fastest keydown performance
26186         if(Roo.isIE){
26187             return function(e){
26188                 var k = e.getKey(), r;
26189                 if(k == e.TAB){
26190                     e.stopEvent();
26191                     r = this.doc.selection.createRange();
26192                     if(r){
26193                         r.collapse(true);
26194                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26195                         this.deferFocus();
26196                     }
26197                     return;
26198                 }
26199                 
26200                 if(k == e.ENTER){
26201                     r = this.doc.selection.createRange();
26202                     if(r){
26203                         var target = r.parentElement();
26204                         if(!target || target.tagName.toLowerCase() != 'li'){
26205                             e.stopEvent();
26206                             r.pasteHTML('<br />');
26207                             r.collapse(false);
26208                             r.select();
26209                         }
26210                     }
26211                 }
26212                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26213                     this.cleanUpPaste.defer(100, this);
26214                     return;
26215                 }
26216                 
26217                 
26218             };
26219         }else if(Roo.isOpera){
26220             return function(e){
26221                 var k = e.getKey();
26222                 if(k == e.TAB){
26223                     e.stopEvent();
26224                     this.win.focus();
26225                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26226                     this.deferFocus();
26227                 }
26228                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26229                     this.cleanUpPaste.defer(100, this);
26230                     return;
26231                 }
26232                 
26233             };
26234         }else if(Roo.isSafari){
26235             return function(e){
26236                 var k = e.getKey();
26237                 
26238                 if(k == e.TAB){
26239                     e.stopEvent();
26240                     this.execCmd('InsertText','\t');
26241                     this.deferFocus();
26242                     return;
26243                 }
26244                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26245                     this.cleanUpPaste.defer(100, this);
26246                     return;
26247                 }
26248                 
26249              };
26250         }
26251     }(),
26252     
26253     getAllAncestors: function()
26254     {
26255         var p = this.getSelectedNode();
26256         var a = [];
26257         if (!p) {
26258             a.push(p); // push blank onto stack..
26259             p = this.getParentElement();
26260         }
26261         
26262         
26263         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26264             a.push(p);
26265             p = p.parentNode;
26266         }
26267         a.push(this.doc.body);
26268         return a;
26269     },
26270     lastSel : false,
26271     lastSelNode : false,
26272     
26273     
26274     getSelection : function() 
26275     {
26276         this.assignDocWin();
26277         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26278     },
26279     
26280     getSelectedNode: function() 
26281     {
26282         // this may only work on Gecko!!!
26283         
26284         // should we cache this!!!!
26285         
26286         
26287         
26288          
26289         var range = this.createRange(this.getSelection()).cloneRange();
26290         
26291         if (Roo.isIE) {
26292             var parent = range.parentElement();
26293             while (true) {
26294                 var testRange = range.duplicate();
26295                 testRange.moveToElementText(parent);
26296                 if (testRange.inRange(range)) {
26297                     break;
26298                 }
26299                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26300                     break;
26301                 }
26302                 parent = parent.parentElement;
26303             }
26304             return parent;
26305         }
26306         
26307         // is ancestor a text element.
26308         var ac =  range.commonAncestorContainer;
26309         if (ac.nodeType == 3) {
26310             ac = ac.parentNode;
26311         }
26312         
26313         var ar = ac.childNodes;
26314          
26315         var nodes = [];
26316         var other_nodes = [];
26317         var has_other_nodes = false;
26318         for (var i=0;i<ar.length;i++) {
26319             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26320                 continue;
26321             }
26322             // fullly contained node.
26323             
26324             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26325                 nodes.push(ar[i]);
26326                 continue;
26327             }
26328             
26329             // probably selected..
26330             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26331                 other_nodes.push(ar[i]);
26332                 continue;
26333             }
26334             // outer..
26335             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26336                 continue;
26337             }
26338             
26339             
26340             has_other_nodes = true;
26341         }
26342         if (!nodes.length && other_nodes.length) {
26343             nodes= other_nodes;
26344         }
26345         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26346             return false;
26347         }
26348         
26349         return nodes[0];
26350     },
26351     createRange: function(sel)
26352     {
26353         // this has strange effects when using with 
26354         // top toolbar - not sure if it's a great idea.
26355         //this.editor.contentWindow.focus();
26356         if (typeof sel != "undefined") {
26357             try {
26358                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26359             } catch(e) {
26360                 return this.doc.createRange();
26361             }
26362         } else {
26363             return this.doc.createRange();
26364         }
26365     },
26366     getParentElement: function()
26367     {
26368         
26369         this.assignDocWin();
26370         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26371         
26372         var range = this.createRange(sel);
26373          
26374         try {
26375             var p = range.commonAncestorContainer;
26376             while (p.nodeType == 3) { // text node
26377                 p = p.parentNode;
26378             }
26379             return p;
26380         } catch (e) {
26381             return null;
26382         }
26383     
26384     },
26385     /***
26386      *
26387      * Range intersection.. the hard stuff...
26388      *  '-1' = before
26389      *  '0' = hits..
26390      *  '1' = after.
26391      *         [ -- selected range --- ]
26392      *   [fail]                        [fail]
26393      *
26394      *    basically..
26395      *      if end is before start or  hits it. fail.
26396      *      if start is after end or hits it fail.
26397      *
26398      *   if either hits (but other is outside. - then it's not 
26399      *   
26400      *    
26401      **/
26402     
26403     
26404     // @see http://www.thismuchiknow.co.uk/?p=64.
26405     rangeIntersectsNode : function(range, node)
26406     {
26407         var nodeRange = node.ownerDocument.createRange();
26408         try {
26409             nodeRange.selectNode(node);
26410         } catch (e) {
26411             nodeRange.selectNodeContents(node);
26412         }
26413     
26414         var rangeStartRange = range.cloneRange();
26415         rangeStartRange.collapse(true);
26416     
26417         var rangeEndRange = range.cloneRange();
26418         rangeEndRange.collapse(false);
26419     
26420         var nodeStartRange = nodeRange.cloneRange();
26421         nodeStartRange.collapse(true);
26422     
26423         var nodeEndRange = nodeRange.cloneRange();
26424         nodeEndRange.collapse(false);
26425     
26426         return rangeStartRange.compareBoundaryPoints(
26427                  Range.START_TO_START, nodeEndRange) == -1 &&
26428                rangeEndRange.compareBoundaryPoints(
26429                  Range.START_TO_START, nodeStartRange) == 1;
26430         
26431          
26432     },
26433     rangeCompareNode : function(range, node)
26434     {
26435         var nodeRange = node.ownerDocument.createRange();
26436         try {
26437             nodeRange.selectNode(node);
26438         } catch (e) {
26439             nodeRange.selectNodeContents(node);
26440         }
26441         
26442         
26443         range.collapse(true);
26444     
26445         nodeRange.collapse(true);
26446      
26447         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26448         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26449          
26450         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26451         
26452         var nodeIsBefore   =  ss == 1;
26453         var nodeIsAfter    = ee == -1;
26454         
26455         if (nodeIsBefore && nodeIsAfter) {
26456             return 0; // outer
26457         }
26458         if (!nodeIsBefore && nodeIsAfter) {
26459             return 1; //right trailed.
26460         }
26461         
26462         if (nodeIsBefore && !nodeIsAfter) {
26463             return 2;  // left trailed.
26464         }
26465         // fully contined.
26466         return 3;
26467     },
26468
26469     // private? - in a new class?
26470     cleanUpPaste :  function()
26471     {
26472         // cleans up the whole document..
26473         Roo.log('cleanuppaste');
26474         
26475         this.cleanUpChildren(this.doc.body);
26476         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26477         if (clean != this.doc.body.innerHTML) {
26478             this.doc.body.innerHTML = clean;
26479         }
26480         
26481     },
26482     
26483     cleanWordChars : function(input) {// change the chars to hex code
26484         var he = Roo.HtmlEditorCore;
26485         
26486         var output = input;
26487         Roo.each(he.swapCodes, function(sw) { 
26488             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26489             
26490             output = output.replace(swapper, sw[1]);
26491         });
26492         
26493         return output;
26494     },
26495     
26496     
26497     cleanUpChildren : function (n)
26498     {
26499         if (!n.childNodes.length) {
26500             return;
26501         }
26502         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26503            this.cleanUpChild(n.childNodes[i]);
26504         }
26505     },
26506     
26507     
26508         
26509     
26510     cleanUpChild : function (node)
26511     {
26512         var ed = this;
26513         //console.log(node);
26514         if (node.nodeName == "#text") {
26515             // clean up silly Windows -- stuff?
26516             return; 
26517         }
26518         if (node.nodeName == "#comment") {
26519             node.parentNode.removeChild(node);
26520             // clean up silly Windows -- stuff?
26521             return; 
26522         }
26523         var lcname = node.tagName.toLowerCase();
26524         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26525         // whitelist of tags..
26526         
26527         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26528             // remove node.
26529             node.parentNode.removeChild(node);
26530             return;
26531             
26532         }
26533         
26534         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26535         
26536         // spans with no attributes - just remove them..
26537         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26538             remove_keep_children = true;
26539         }
26540         
26541         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26542         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26543         
26544         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26545         //    remove_keep_children = true;
26546         //}
26547         
26548         if (remove_keep_children) {
26549             this.cleanUpChildren(node);
26550             // inserts everything just before this node...
26551             while (node.childNodes.length) {
26552                 var cn = node.childNodes[0];
26553                 node.removeChild(cn);
26554                 node.parentNode.insertBefore(cn, node);
26555             }
26556             node.parentNode.removeChild(node);
26557             return;
26558         }
26559         
26560         if (!node.attributes || !node.attributes.length) {
26561             
26562           
26563             
26564             
26565             this.cleanUpChildren(node);
26566             return;
26567         }
26568         
26569         function cleanAttr(n,v)
26570         {
26571             
26572             if (v.match(/^\./) || v.match(/^\//)) {
26573                 return;
26574             }
26575             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26576                 return;
26577             }
26578             if (v.match(/^#/)) {
26579                 return;
26580             }
26581             if (v.match(/^\{/)) { // allow template editing.
26582                 return;
26583             }
26584 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26585             node.removeAttribute(n);
26586             
26587         }
26588         
26589         var cwhite = this.cwhite;
26590         var cblack = this.cblack;
26591             
26592         function cleanStyle(n,v)
26593         {
26594             if (v.match(/expression/)) { //XSS?? should we even bother..
26595                 node.removeAttribute(n);
26596                 return;
26597             }
26598             
26599             var parts = v.split(/;/);
26600             var clean = [];
26601             
26602             Roo.each(parts, function(p) {
26603                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26604                 if (!p.length) {
26605                     return true;
26606                 }
26607                 var l = p.split(':').shift().replace(/\s+/g,'');
26608                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26609                 
26610                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26611 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26612                     //node.removeAttribute(n);
26613                     return true;
26614                 }
26615                 //Roo.log()
26616                 // only allow 'c whitelisted system attributes'
26617                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26618 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26619                     //node.removeAttribute(n);
26620                     return true;
26621                 }
26622                 
26623                 
26624                  
26625                 
26626                 clean.push(p);
26627                 return true;
26628             });
26629             if (clean.length) { 
26630                 node.setAttribute(n, clean.join(';'));
26631             } else {
26632                 node.removeAttribute(n);
26633             }
26634             
26635         }
26636         
26637         
26638         for (var i = node.attributes.length-1; i > -1 ; i--) {
26639             var a = node.attributes[i];
26640             //console.log(a);
26641             
26642             if (a.name.toLowerCase().substr(0,2)=='on')  {
26643                 node.removeAttribute(a.name);
26644                 continue;
26645             }
26646             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26647                 node.removeAttribute(a.name);
26648                 continue;
26649             }
26650             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26651                 cleanAttr(a.name,a.value); // fixme..
26652                 continue;
26653             }
26654             if (a.name == 'style') {
26655                 cleanStyle(a.name,a.value);
26656                 continue;
26657             }
26658             /// clean up MS crap..
26659             // tecnically this should be a list of valid class'es..
26660             
26661             
26662             if (a.name == 'class') {
26663                 if (a.value.match(/^Mso/)) {
26664                     node.removeAttribute('class');
26665                 }
26666                 
26667                 if (a.value.match(/^body$/)) {
26668                     node.removeAttribute('class');
26669                 }
26670                 continue;
26671             }
26672             
26673             // style cleanup!?
26674             // class cleanup?
26675             
26676         }
26677         
26678         
26679         this.cleanUpChildren(node);
26680         
26681         
26682     },
26683     
26684     /**
26685      * Clean up MS wordisms...
26686      */
26687     cleanWord : function(node)
26688     {
26689         if (!node) {
26690             this.cleanWord(this.doc.body);
26691             return;
26692         }
26693         
26694         if(
26695                 node.nodeName == 'SPAN' &&
26696                 !node.hasAttributes() &&
26697                 node.childNodes.length == 1 &&
26698                 node.firstChild.nodeName == "#text"  
26699         ) {
26700             var textNode = node.firstChild;
26701             node.removeChild(textNode);
26702             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26703                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26704             }
26705             node.parentNode.insertBefore(textNode, node);
26706             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26707                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26708             }
26709             node.parentNode.removeChild(node);
26710         }
26711         
26712         if (node.nodeName == "#text") {
26713             // clean up silly Windows -- stuff?
26714             return; 
26715         }
26716         if (node.nodeName == "#comment") {
26717             node.parentNode.removeChild(node);
26718             // clean up silly Windows -- stuff?
26719             return; 
26720         }
26721         
26722         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26723             node.parentNode.removeChild(node);
26724             return;
26725         }
26726         //Roo.log(node.tagName);
26727         // remove - but keep children..
26728         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26729             //Roo.log('-- removed');
26730             while (node.childNodes.length) {
26731                 var cn = node.childNodes[0];
26732                 node.removeChild(cn);
26733                 node.parentNode.insertBefore(cn, node);
26734                 // move node to parent - and clean it..
26735                 this.cleanWord(cn);
26736             }
26737             node.parentNode.removeChild(node);
26738             /// no need to iterate chidlren = it's got none..
26739             //this.iterateChildren(node, this.cleanWord);
26740             return;
26741         }
26742         // clean styles
26743         if (node.className.length) {
26744             
26745             var cn = node.className.split(/\W+/);
26746             var cna = [];
26747             Roo.each(cn, function(cls) {
26748                 if (cls.match(/Mso[a-zA-Z]+/)) {
26749                     return;
26750                 }
26751                 cna.push(cls);
26752             });
26753             node.className = cna.length ? cna.join(' ') : '';
26754             if (!cna.length) {
26755                 node.removeAttribute("class");
26756             }
26757         }
26758         
26759         if (node.hasAttribute("lang")) {
26760             node.removeAttribute("lang");
26761         }
26762         
26763         if (node.hasAttribute("style")) {
26764             
26765             var styles = node.getAttribute("style").split(";");
26766             var nstyle = [];
26767             Roo.each(styles, function(s) {
26768                 if (!s.match(/:/)) {
26769                     return;
26770                 }
26771                 var kv = s.split(":");
26772                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26773                     return;
26774                 }
26775                 // what ever is left... we allow.
26776                 nstyle.push(s);
26777             });
26778             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26779             if (!nstyle.length) {
26780                 node.removeAttribute('style');
26781             }
26782         }
26783         this.iterateChildren(node, this.cleanWord);
26784         
26785         
26786         
26787     },
26788     /**
26789      * iterateChildren of a Node, calling fn each time, using this as the scole..
26790      * @param {DomNode} node node to iterate children of.
26791      * @param {Function} fn method of this class to call on each item.
26792      */
26793     iterateChildren : function(node, fn)
26794     {
26795         if (!node.childNodes.length) {
26796                 return;
26797         }
26798         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26799            fn.call(this, node.childNodes[i])
26800         }
26801     },
26802     
26803     
26804     /**
26805      * cleanTableWidths.
26806      *
26807      * Quite often pasting from word etc.. results in tables with column and widths.
26808      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26809      *
26810      */
26811     cleanTableWidths : function(node)
26812     {
26813          
26814          
26815         if (!node) {
26816             this.cleanTableWidths(this.doc.body);
26817             return;
26818         }
26819         
26820         // ignore list...
26821         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26822             return; 
26823         }
26824         Roo.log(node.tagName);
26825         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26826             this.iterateChildren(node, this.cleanTableWidths);
26827             return;
26828         }
26829         if (node.hasAttribute('width')) {
26830             node.removeAttribute('width');
26831         }
26832         
26833          
26834         if (node.hasAttribute("style")) {
26835             // pretty basic...
26836             
26837             var styles = node.getAttribute("style").split(";");
26838             var nstyle = [];
26839             Roo.each(styles, function(s) {
26840                 if (!s.match(/:/)) {
26841                     return;
26842                 }
26843                 var kv = s.split(":");
26844                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26845                     return;
26846                 }
26847                 // what ever is left... we allow.
26848                 nstyle.push(s);
26849             });
26850             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26851             if (!nstyle.length) {
26852                 node.removeAttribute('style');
26853             }
26854         }
26855         
26856         this.iterateChildren(node, this.cleanTableWidths);
26857         
26858         
26859     },
26860     
26861     
26862     
26863     
26864     domToHTML : function(currentElement, depth, nopadtext) {
26865         
26866         depth = depth || 0;
26867         nopadtext = nopadtext || false;
26868     
26869         if (!currentElement) {
26870             return this.domToHTML(this.doc.body);
26871         }
26872         
26873         //Roo.log(currentElement);
26874         var j;
26875         var allText = false;
26876         var nodeName = currentElement.nodeName;
26877         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26878         
26879         if  (nodeName == '#text') {
26880             
26881             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26882         }
26883         
26884         
26885         var ret = '';
26886         if (nodeName != 'BODY') {
26887              
26888             var i = 0;
26889             // Prints the node tagName, such as <A>, <IMG>, etc
26890             if (tagName) {
26891                 var attr = [];
26892                 for(i = 0; i < currentElement.attributes.length;i++) {
26893                     // quoting?
26894                     var aname = currentElement.attributes.item(i).name;
26895                     if (!currentElement.attributes.item(i).value.length) {
26896                         continue;
26897                     }
26898                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26899                 }
26900                 
26901                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26902             } 
26903             else {
26904                 
26905                 // eack
26906             }
26907         } else {
26908             tagName = false;
26909         }
26910         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26911             return ret;
26912         }
26913         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26914             nopadtext = true;
26915         }
26916         
26917         
26918         // Traverse the tree
26919         i = 0;
26920         var currentElementChild = currentElement.childNodes.item(i);
26921         var allText = true;
26922         var innerHTML  = '';
26923         lastnode = '';
26924         while (currentElementChild) {
26925             // Formatting code (indent the tree so it looks nice on the screen)
26926             var nopad = nopadtext;
26927             if (lastnode == 'SPAN') {
26928                 nopad  = true;
26929             }
26930             // text
26931             if  (currentElementChild.nodeName == '#text') {
26932                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26933                 toadd = nopadtext ? toadd : toadd.trim();
26934                 if (!nopad && toadd.length > 80) {
26935                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26936                 }
26937                 innerHTML  += toadd;
26938                 
26939                 i++;
26940                 currentElementChild = currentElement.childNodes.item(i);
26941                 lastNode = '';
26942                 continue;
26943             }
26944             allText = false;
26945             
26946             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26947                 
26948             // Recursively traverse the tree structure of the child node
26949             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26950             lastnode = currentElementChild.nodeName;
26951             i++;
26952             currentElementChild=currentElement.childNodes.item(i);
26953         }
26954         
26955         ret += innerHTML;
26956         
26957         if (!allText) {
26958                 // The remaining code is mostly for formatting the tree
26959             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26960         }
26961         
26962         
26963         if (tagName) {
26964             ret+= "</"+tagName+">";
26965         }
26966         return ret;
26967         
26968     },
26969         
26970     applyBlacklists : function()
26971     {
26972         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26973         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26974         
26975         this.white = [];
26976         this.black = [];
26977         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26978             if (b.indexOf(tag) > -1) {
26979                 return;
26980             }
26981             this.white.push(tag);
26982             
26983         }, this);
26984         
26985         Roo.each(w, function(tag) {
26986             if (b.indexOf(tag) > -1) {
26987                 return;
26988             }
26989             if (this.white.indexOf(tag) > -1) {
26990                 return;
26991             }
26992             this.white.push(tag);
26993             
26994         }, this);
26995         
26996         
26997         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26998             if (w.indexOf(tag) > -1) {
26999                 return;
27000             }
27001             this.black.push(tag);
27002             
27003         }, this);
27004         
27005         Roo.each(b, function(tag) {
27006             if (w.indexOf(tag) > -1) {
27007                 return;
27008             }
27009             if (this.black.indexOf(tag) > -1) {
27010                 return;
27011             }
27012             this.black.push(tag);
27013             
27014         }, this);
27015         
27016         
27017         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27018         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27019         
27020         this.cwhite = [];
27021         this.cblack = [];
27022         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27023             if (b.indexOf(tag) > -1) {
27024                 return;
27025             }
27026             this.cwhite.push(tag);
27027             
27028         }, this);
27029         
27030         Roo.each(w, function(tag) {
27031             if (b.indexOf(tag) > -1) {
27032                 return;
27033             }
27034             if (this.cwhite.indexOf(tag) > -1) {
27035                 return;
27036             }
27037             this.cwhite.push(tag);
27038             
27039         }, this);
27040         
27041         
27042         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27043             if (w.indexOf(tag) > -1) {
27044                 return;
27045             }
27046             this.cblack.push(tag);
27047             
27048         }, this);
27049         
27050         Roo.each(b, function(tag) {
27051             if (w.indexOf(tag) > -1) {
27052                 return;
27053             }
27054             if (this.cblack.indexOf(tag) > -1) {
27055                 return;
27056             }
27057             this.cblack.push(tag);
27058             
27059         }, this);
27060     },
27061     
27062     setStylesheets : function(stylesheets)
27063     {
27064         if(typeof(stylesheets) == 'string'){
27065             Roo.get(this.iframe.contentDocument.head).createChild({
27066                 tag : 'link',
27067                 rel : 'stylesheet',
27068                 type : 'text/css',
27069                 href : stylesheets
27070             });
27071             
27072             return;
27073         }
27074         var _this = this;
27075      
27076         Roo.each(stylesheets, function(s) {
27077             if(!s.length){
27078                 return;
27079             }
27080             
27081             Roo.get(_this.iframe.contentDocument.head).createChild({
27082                 tag : 'link',
27083                 rel : 'stylesheet',
27084                 type : 'text/css',
27085                 href : s
27086             });
27087         });
27088
27089         
27090     },
27091     
27092     removeStylesheets : function()
27093     {
27094         var _this = this;
27095         
27096         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27097             s.remove();
27098         });
27099     },
27100     
27101     setStyle : function(style)
27102     {
27103         Roo.get(this.iframe.contentDocument.head).createChild({
27104             tag : 'style',
27105             type : 'text/css',
27106             html : style
27107         });
27108
27109         return;
27110     }
27111     
27112     // hide stuff that is not compatible
27113     /**
27114      * @event blur
27115      * @hide
27116      */
27117     /**
27118      * @event change
27119      * @hide
27120      */
27121     /**
27122      * @event focus
27123      * @hide
27124      */
27125     /**
27126      * @event specialkey
27127      * @hide
27128      */
27129     /**
27130      * @cfg {String} fieldClass @hide
27131      */
27132     /**
27133      * @cfg {String} focusClass @hide
27134      */
27135     /**
27136      * @cfg {String} autoCreate @hide
27137      */
27138     /**
27139      * @cfg {String} inputType @hide
27140      */
27141     /**
27142      * @cfg {String} invalidClass @hide
27143      */
27144     /**
27145      * @cfg {String} invalidText @hide
27146      */
27147     /**
27148      * @cfg {String} msgFx @hide
27149      */
27150     /**
27151      * @cfg {String} validateOnBlur @hide
27152      */
27153 });
27154
27155 Roo.HtmlEditorCore.white = [
27156         'area', 'br', 'img', 'input', 'hr', 'wbr',
27157         
27158        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27159        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27160        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27161        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27162        'table',   'ul',         'xmp', 
27163        
27164        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27165       'thead',   'tr', 
27166      
27167       'dir', 'menu', 'ol', 'ul', 'dl',
27168        
27169       'embed',  'object'
27170 ];
27171
27172
27173 Roo.HtmlEditorCore.black = [
27174     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27175         'applet', // 
27176         'base',   'basefont', 'bgsound', 'blink',  'body', 
27177         'frame',  'frameset', 'head',    'html',   'ilayer', 
27178         'iframe', 'layer',  'link',     'meta',    'object',   
27179         'script', 'style' ,'title',  'xml' // clean later..
27180 ];
27181 Roo.HtmlEditorCore.clean = [
27182     'script', 'style', 'title', 'xml'
27183 ];
27184 Roo.HtmlEditorCore.remove = [
27185     'font'
27186 ];
27187 // attributes..
27188
27189 Roo.HtmlEditorCore.ablack = [
27190     'on'
27191 ];
27192     
27193 Roo.HtmlEditorCore.aclean = [ 
27194     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27195 ];
27196
27197 // protocols..
27198 Roo.HtmlEditorCore.pwhite= [
27199         'http',  'https',  'mailto'
27200 ];
27201
27202 // white listed style attributes.
27203 Roo.HtmlEditorCore.cwhite= [
27204       //  'text-align', /// default is to allow most things..
27205       
27206          
27207 //        'font-size'//??
27208 ];
27209
27210 // black listed style attributes.
27211 Roo.HtmlEditorCore.cblack= [
27212       //  'font-size' -- this can be set by the project 
27213 ];
27214
27215
27216 Roo.HtmlEditorCore.swapCodes   =[ 
27217     [    8211, "&#8211;" ], 
27218     [    8212, "&#8212;" ], 
27219     [    8216,  "'" ],  
27220     [    8217, "'" ],  
27221     [    8220, '"' ],  
27222     [    8221, '"' ],  
27223     [    8226, "*" ],  
27224     [    8230, "..." ]
27225 ]; 
27226
27227     /*
27228  * - LGPL
27229  *
27230  * HtmlEditor
27231  * 
27232  */
27233
27234 /**
27235  * @class Roo.bootstrap.HtmlEditor
27236  * @extends Roo.bootstrap.TextArea
27237  * Bootstrap HtmlEditor class
27238
27239  * @constructor
27240  * Create a new HtmlEditor
27241  * @param {Object} config The config object
27242  */
27243
27244 Roo.bootstrap.HtmlEditor = function(config){
27245     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27246     if (!this.toolbars) {
27247         this.toolbars = [];
27248     }
27249     
27250     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27251     this.addEvents({
27252             /**
27253              * @event initialize
27254              * Fires when the editor is fully initialized (including the iframe)
27255              * @param {HtmlEditor} this
27256              */
27257             initialize: true,
27258             /**
27259              * @event activate
27260              * Fires when the editor is first receives the focus. Any insertion must wait
27261              * until after this event.
27262              * @param {HtmlEditor} this
27263              */
27264             activate: true,
27265              /**
27266              * @event beforesync
27267              * Fires before the textarea is updated with content from the editor iframe. Return false
27268              * to cancel the sync.
27269              * @param {HtmlEditor} this
27270              * @param {String} html
27271              */
27272             beforesync: true,
27273              /**
27274              * @event beforepush
27275              * Fires before the iframe editor is updated with content from the textarea. Return false
27276              * to cancel the push.
27277              * @param {HtmlEditor} this
27278              * @param {String} html
27279              */
27280             beforepush: true,
27281              /**
27282              * @event sync
27283              * Fires when the textarea is updated with content from the editor iframe.
27284              * @param {HtmlEditor} this
27285              * @param {String} html
27286              */
27287             sync: true,
27288              /**
27289              * @event push
27290              * Fires when the iframe editor is updated with content from the textarea.
27291              * @param {HtmlEditor} this
27292              * @param {String} html
27293              */
27294             push: true,
27295              /**
27296              * @event editmodechange
27297              * Fires when the editor switches edit modes
27298              * @param {HtmlEditor} this
27299              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27300              */
27301             editmodechange: true,
27302             /**
27303              * @event editorevent
27304              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27305              * @param {HtmlEditor} this
27306              */
27307             editorevent: true,
27308             /**
27309              * @event firstfocus
27310              * Fires when on first focus - needed by toolbars..
27311              * @param {HtmlEditor} this
27312              */
27313             firstfocus: true,
27314             /**
27315              * @event autosave
27316              * Auto save the htmlEditor value as a file into Events
27317              * @param {HtmlEditor} this
27318              */
27319             autosave: true,
27320             /**
27321              * @event savedpreview
27322              * preview the saved version of htmlEditor
27323              * @param {HtmlEditor} this
27324              */
27325             savedpreview: true
27326         });
27327 };
27328
27329
27330 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27331     
27332     
27333       /**
27334      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27335      */
27336     toolbars : false,
27337     
27338      /**
27339     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27340     */
27341     btns : [],
27342    
27343      /**
27344      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27345      *                        Roo.resizable.
27346      */
27347     resizable : false,
27348      /**
27349      * @cfg {Number} height (in pixels)
27350      */   
27351     height: 300,
27352    /**
27353      * @cfg {Number} width (in pixels)
27354      */   
27355     width: false,
27356     
27357     /**
27358      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27359      * 
27360      */
27361     stylesheets: false,
27362     
27363     // id of frame..
27364     frameId: false,
27365     
27366     // private properties
27367     validationEvent : false,
27368     deferHeight: true,
27369     initialized : false,
27370     activated : false,
27371     
27372     onFocus : Roo.emptyFn,
27373     iframePad:3,
27374     hideMode:'offsets',
27375     
27376     tbContainer : false,
27377     
27378     bodyCls : '',
27379     
27380     toolbarContainer :function() {
27381         return this.wrap.select('.x-html-editor-tb',true).first();
27382     },
27383
27384     /**
27385      * Protected method that will not generally be called directly. It
27386      * is called when the editor creates its toolbar. Override this method if you need to
27387      * add custom toolbar buttons.
27388      * @param {HtmlEditor} editor
27389      */
27390     createToolbar : function(){
27391         Roo.log('renewing');
27392         Roo.log("create toolbars");
27393         
27394         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27395         this.toolbars[0].render(this.toolbarContainer());
27396         
27397         return;
27398         
27399 //        if (!editor.toolbars || !editor.toolbars.length) {
27400 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27401 //        }
27402 //        
27403 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27404 //            editor.toolbars[i] = Roo.factory(
27405 //                    typeof(editor.toolbars[i]) == 'string' ?
27406 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27407 //                Roo.bootstrap.HtmlEditor);
27408 //            editor.toolbars[i].init(editor);
27409 //        }
27410     },
27411
27412      
27413     // private
27414     onRender : function(ct, position)
27415     {
27416        // Roo.log("Call onRender: " + this.xtype);
27417         var _t = this;
27418         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27419       
27420         this.wrap = this.inputEl().wrap({
27421             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27422         });
27423         
27424         this.editorcore.onRender(ct, position);
27425          
27426         if (this.resizable) {
27427             this.resizeEl = new Roo.Resizable(this.wrap, {
27428                 pinned : true,
27429                 wrap: true,
27430                 dynamic : true,
27431                 minHeight : this.height,
27432                 height: this.height,
27433                 handles : this.resizable,
27434                 width: this.width,
27435                 listeners : {
27436                     resize : function(r, w, h) {
27437                         _t.onResize(w,h); // -something
27438                     }
27439                 }
27440             });
27441             
27442         }
27443         this.createToolbar(this);
27444        
27445         
27446         if(!this.width && this.resizable){
27447             this.setSize(this.wrap.getSize());
27448         }
27449         if (this.resizeEl) {
27450             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27451             // should trigger onReize..
27452         }
27453         
27454     },
27455
27456     // private
27457     onResize : function(w, h)
27458     {
27459         Roo.log('resize: ' +w + ',' + h );
27460         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27461         var ew = false;
27462         var eh = false;
27463         
27464         if(this.inputEl() ){
27465             if(typeof w == 'number'){
27466                 var aw = w - this.wrap.getFrameWidth('lr');
27467                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27468                 ew = aw;
27469             }
27470             if(typeof h == 'number'){
27471                  var tbh = -11;  // fixme it needs to tool bar size!
27472                 for (var i =0; i < this.toolbars.length;i++) {
27473                     // fixme - ask toolbars for heights?
27474                     tbh += this.toolbars[i].el.getHeight();
27475                     //if (this.toolbars[i].footer) {
27476                     //    tbh += this.toolbars[i].footer.el.getHeight();
27477                     //}
27478                 }
27479               
27480                 
27481                 
27482                 
27483                 
27484                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27485                 ah -= 5; // knock a few pixes off for look..
27486                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27487                 var eh = ah;
27488             }
27489         }
27490         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27491         this.editorcore.onResize(ew,eh);
27492         
27493     },
27494
27495     /**
27496      * Toggles the editor between standard and source edit mode.
27497      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27498      */
27499     toggleSourceEdit : function(sourceEditMode)
27500     {
27501         this.editorcore.toggleSourceEdit(sourceEditMode);
27502         
27503         if(this.editorcore.sourceEditMode){
27504             Roo.log('editor - showing textarea');
27505             
27506 //            Roo.log('in');
27507 //            Roo.log(this.syncValue());
27508             this.syncValue();
27509             this.inputEl().removeClass(['hide', 'x-hidden']);
27510             this.inputEl().dom.removeAttribute('tabIndex');
27511             this.inputEl().focus();
27512         }else{
27513             Roo.log('editor - hiding textarea');
27514 //            Roo.log('out')
27515 //            Roo.log(this.pushValue()); 
27516             this.pushValue();
27517             
27518             this.inputEl().addClass(['hide', 'x-hidden']);
27519             this.inputEl().dom.setAttribute('tabIndex', -1);
27520             //this.deferFocus();
27521         }
27522          
27523         if(this.resizable){
27524             this.setSize(this.wrap.getSize());
27525         }
27526         
27527         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27528     },
27529  
27530     // private (for BoxComponent)
27531     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27532
27533     // private (for BoxComponent)
27534     getResizeEl : function(){
27535         return this.wrap;
27536     },
27537
27538     // private (for BoxComponent)
27539     getPositionEl : function(){
27540         return this.wrap;
27541     },
27542
27543     // private
27544     initEvents : function(){
27545         this.originalValue = this.getValue();
27546     },
27547
27548 //    /**
27549 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27550 //     * @method
27551 //     */
27552 //    markInvalid : Roo.emptyFn,
27553 //    /**
27554 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27555 //     * @method
27556 //     */
27557 //    clearInvalid : Roo.emptyFn,
27558
27559     setValue : function(v){
27560         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27561         this.editorcore.pushValue();
27562     },
27563
27564      
27565     // private
27566     deferFocus : function(){
27567         this.focus.defer(10, this);
27568     },
27569
27570     // doc'ed in Field
27571     focus : function(){
27572         this.editorcore.focus();
27573         
27574     },
27575       
27576
27577     // private
27578     onDestroy : function(){
27579         
27580         
27581         
27582         if(this.rendered){
27583             
27584             for (var i =0; i < this.toolbars.length;i++) {
27585                 // fixme - ask toolbars for heights?
27586                 this.toolbars[i].onDestroy();
27587             }
27588             
27589             this.wrap.dom.innerHTML = '';
27590             this.wrap.remove();
27591         }
27592     },
27593
27594     // private
27595     onFirstFocus : function(){
27596         //Roo.log("onFirstFocus");
27597         this.editorcore.onFirstFocus();
27598          for (var i =0; i < this.toolbars.length;i++) {
27599             this.toolbars[i].onFirstFocus();
27600         }
27601         
27602     },
27603     
27604     // private
27605     syncValue : function()
27606     {   
27607         this.editorcore.syncValue();
27608     },
27609     
27610     pushValue : function()
27611     {   
27612         this.editorcore.pushValue();
27613     }
27614      
27615     
27616     // hide stuff that is not compatible
27617     /**
27618      * @event blur
27619      * @hide
27620      */
27621     /**
27622      * @event change
27623      * @hide
27624      */
27625     /**
27626      * @event focus
27627      * @hide
27628      */
27629     /**
27630      * @event specialkey
27631      * @hide
27632      */
27633     /**
27634      * @cfg {String} fieldClass @hide
27635      */
27636     /**
27637      * @cfg {String} focusClass @hide
27638      */
27639     /**
27640      * @cfg {String} autoCreate @hide
27641      */
27642     /**
27643      * @cfg {String} inputType @hide
27644      */
27645      
27646     /**
27647      * @cfg {String} invalidText @hide
27648      */
27649     /**
27650      * @cfg {String} msgFx @hide
27651      */
27652     /**
27653      * @cfg {String} validateOnBlur @hide
27654      */
27655 });
27656  
27657     
27658    
27659    
27660    
27661       
27662 Roo.namespace('Roo.bootstrap.htmleditor');
27663 /**
27664  * @class Roo.bootstrap.HtmlEditorToolbar1
27665  * Basic Toolbar
27666  * 
27667  * @example
27668  * Usage:
27669  *
27670  new Roo.bootstrap.HtmlEditor({
27671     ....
27672     toolbars : [
27673         new Roo.bootstrap.HtmlEditorToolbar1({
27674             disable : { fonts: 1 , format: 1, ..., ... , ...],
27675             btns : [ .... ]
27676         })
27677     }
27678      
27679  * 
27680  * @cfg {Object} disable List of elements to disable..
27681  * @cfg {Array} btns List of additional buttons.
27682  * 
27683  * 
27684  * NEEDS Extra CSS? 
27685  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27686  */
27687  
27688 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27689 {
27690     
27691     Roo.apply(this, config);
27692     
27693     // default disabled, based on 'good practice'..
27694     this.disable = this.disable || {};
27695     Roo.applyIf(this.disable, {
27696         fontSize : true,
27697         colors : true,
27698         specialElements : true
27699     });
27700     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27701     
27702     this.editor = config.editor;
27703     this.editorcore = config.editor.editorcore;
27704     
27705     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27706     
27707     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27708     // dont call parent... till later.
27709 }
27710 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27711      
27712     bar : true,
27713     
27714     editor : false,
27715     editorcore : false,
27716     
27717     
27718     formats : [
27719         "p" ,  
27720         "h1","h2","h3","h4","h5","h6", 
27721         "pre", "code", 
27722         "abbr", "acronym", "address", "cite", "samp", "var",
27723         'div','span'
27724     ],
27725     
27726     onRender : function(ct, position)
27727     {
27728        // Roo.log("Call onRender: " + this.xtype);
27729         
27730        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27731        Roo.log(this.el);
27732        this.el.dom.style.marginBottom = '0';
27733        var _this = this;
27734        var editorcore = this.editorcore;
27735        var editor= this.editor;
27736        
27737        var children = [];
27738        var btn = function(id,cmd , toggle, handler, html){
27739        
27740             var  event = toggle ? 'toggle' : 'click';
27741        
27742             var a = {
27743                 size : 'sm',
27744                 xtype: 'Button',
27745                 xns: Roo.bootstrap,
27746                 //glyphicon : id,
27747                 fa: id,
27748                 cmd : id || cmd,
27749                 enableToggle:toggle !== false,
27750                 html : html || '',
27751                 pressed : toggle ? false : null,
27752                 listeners : {}
27753             };
27754             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27755                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27756             };
27757             children.push(a);
27758             return a;
27759        }
27760        
27761     //    var cb_box = function...
27762         
27763         var style = {
27764                 xtype: 'Button',
27765                 size : 'sm',
27766                 xns: Roo.bootstrap,
27767                 fa : 'font',
27768                 //html : 'submit'
27769                 menu : {
27770                     xtype: 'Menu',
27771                     xns: Roo.bootstrap,
27772                     items:  []
27773                 }
27774         };
27775         Roo.each(this.formats, function(f) {
27776             style.menu.items.push({
27777                 xtype :'MenuItem',
27778                 xns: Roo.bootstrap,
27779                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27780                 tagname : f,
27781                 listeners : {
27782                     click : function()
27783                     {
27784                         editorcore.insertTag(this.tagname);
27785                         editor.focus();
27786                     }
27787                 }
27788                 
27789             });
27790         });
27791         children.push(style);   
27792         
27793         btn('bold',false,true);
27794         btn('italic',false,true);
27795         btn('align-left', 'justifyleft',true);
27796         btn('align-center', 'justifycenter',true);
27797         btn('align-right' , 'justifyright',true);
27798         btn('link', false, false, function(btn) {
27799             //Roo.log("create link?");
27800             var url = prompt(this.createLinkText, this.defaultLinkValue);
27801             if(url && url != 'http:/'+'/'){
27802                 this.editorcore.relayCmd('createlink', url);
27803             }
27804         }),
27805         btn('list','insertunorderedlist',true);
27806         btn('pencil', false,true, function(btn){
27807                 Roo.log(this);
27808                 this.toggleSourceEdit(btn.pressed);
27809         });
27810         
27811         if (this.editor.btns.length > 0) {
27812             for (var i = 0; i<this.editor.btns.length; i++) {
27813                 children.push(this.editor.btns[i]);
27814             }
27815         }
27816         
27817         /*
27818         var cog = {
27819                 xtype: 'Button',
27820                 size : 'sm',
27821                 xns: Roo.bootstrap,
27822                 glyphicon : 'cog',
27823                 //html : 'submit'
27824                 menu : {
27825                     xtype: 'Menu',
27826                     xns: Roo.bootstrap,
27827                     items:  []
27828                 }
27829         };
27830         
27831         cog.menu.items.push({
27832             xtype :'MenuItem',
27833             xns: Roo.bootstrap,
27834             html : Clean styles,
27835             tagname : f,
27836             listeners : {
27837                 click : function()
27838                 {
27839                     editorcore.insertTag(this.tagname);
27840                     editor.focus();
27841                 }
27842             }
27843             
27844         });
27845        */
27846         
27847          
27848        this.xtype = 'NavSimplebar';
27849         
27850         for(var i=0;i< children.length;i++) {
27851             
27852             this.buttons.add(this.addxtypeChild(children[i]));
27853             
27854         }
27855         
27856         editor.on('editorevent', this.updateToolbar, this);
27857     },
27858     onBtnClick : function(id)
27859     {
27860        this.editorcore.relayCmd(id);
27861        this.editorcore.focus();
27862     },
27863     
27864     /**
27865      * Protected method that will not generally be called directly. It triggers
27866      * a toolbar update by reading the markup state of the current selection in the editor.
27867      */
27868     updateToolbar: function(){
27869
27870         if(!this.editorcore.activated){
27871             this.editor.onFirstFocus(); // is this neeed?
27872             return;
27873         }
27874
27875         var btns = this.buttons; 
27876         var doc = this.editorcore.doc;
27877         btns.get('bold').setActive(doc.queryCommandState('bold'));
27878         btns.get('italic').setActive(doc.queryCommandState('italic'));
27879         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27880         
27881         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27882         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27883         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27884         
27885         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27886         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27887          /*
27888         
27889         var ans = this.editorcore.getAllAncestors();
27890         if (this.formatCombo) {
27891             
27892             
27893             var store = this.formatCombo.store;
27894             this.formatCombo.setValue("");
27895             for (var i =0; i < ans.length;i++) {
27896                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27897                     // select it..
27898                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27899                     break;
27900                 }
27901             }
27902         }
27903         
27904         
27905         
27906         // hides menus... - so this cant be on a menu...
27907         Roo.bootstrap.MenuMgr.hideAll();
27908         */
27909         Roo.bootstrap.MenuMgr.hideAll();
27910         //this.editorsyncValue();
27911     },
27912     onFirstFocus: function() {
27913         this.buttons.each(function(item){
27914            item.enable();
27915         });
27916     },
27917     toggleSourceEdit : function(sourceEditMode){
27918         
27919           
27920         if(sourceEditMode){
27921             Roo.log("disabling buttons");
27922            this.buttons.each( function(item){
27923                 if(item.cmd != 'pencil'){
27924                     item.disable();
27925                 }
27926             });
27927           
27928         }else{
27929             Roo.log("enabling buttons");
27930             if(this.editorcore.initialized){
27931                 this.buttons.each( function(item){
27932                     item.enable();
27933                 });
27934             }
27935             
27936         }
27937         Roo.log("calling toggole on editor");
27938         // tell the editor that it's been pressed..
27939         this.editor.toggleSourceEdit(sourceEditMode);
27940        
27941     }
27942 });
27943
27944
27945
27946
27947  
27948 /*
27949  * - LGPL
27950  */
27951
27952 /**
27953  * @class Roo.bootstrap.Markdown
27954  * @extends Roo.bootstrap.TextArea
27955  * Bootstrap Showdown editable area
27956  * @cfg {string} content
27957  * 
27958  * @constructor
27959  * Create a new Showdown
27960  */
27961
27962 Roo.bootstrap.Markdown = function(config){
27963     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27964    
27965 };
27966
27967 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27968     
27969     editing :false,
27970     
27971     initEvents : function()
27972     {
27973         
27974         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27975         this.markdownEl = this.el.createChild({
27976             cls : 'roo-markdown-area'
27977         });
27978         this.inputEl().addClass('d-none');
27979         if (this.getValue() == '') {
27980             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27981             
27982         } else {
27983             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27984         }
27985         this.markdownEl.on('click', this.toggleTextEdit, this);
27986         this.on('blur', this.toggleTextEdit, this);
27987         this.on('specialkey', this.resizeTextArea, this);
27988     },
27989     
27990     toggleTextEdit : function()
27991     {
27992         var sh = this.markdownEl.getHeight();
27993         this.inputEl().addClass('d-none');
27994         this.markdownEl.addClass('d-none');
27995         if (!this.editing) {
27996             // show editor?
27997             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27998             this.inputEl().removeClass('d-none');
27999             this.inputEl().focus();
28000             this.editing = true;
28001             return;
28002         }
28003         // show showdown...
28004         this.updateMarkdown();
28005         this.markdownEl.removeClass('d-none');
28006         this.editing = false;
28007         return;
28008     },
28009     updateMarkdown : function()
28010     {
28011         if (this.getValue() == '') {
28012             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28013             return;
28014         }
28015  
28016         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28017     },
28018     
28019     resizeTextArea: function () {
28020         
28021         var sh = 100;
28022         Roo.log([sh, this.getValue().split("\n").length * 30]);
28023         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28024     },
28025     setValue : function(val)
28026     {
28027         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28028         if (!this.editing) {
28029             this.updateMarkdown();
28030         }
28031         
28032     },
28033     focus : function()
28034     {
28035         if (!this.editing) {
28036             this.toggleTextEdit();
28037         }
28038         
28039     }
28040
28041
28042 });/*
28043  * Based on:
28044  * Ext JS Library 1.1.1
28045  * Copyright(c) 2006-2007, Ext JS, LLC.
28046  *
28047  * Originally Released Under LGPL - original licence link has changed is not relivant.
28048  *
28049  * Fork - LGPL
28050  * <script type="text/javascript">
28051  */
28052  
28053 /**
28054  * @class Roo.bootstrap.PagingToolbar
28055  * @extends Roo.bootstrap.NavSimplebar
28056  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28057  * @constructor
28058  * Create a new PagingToolbar
28059  * @param {Object} config The config object
28060  * @param {Roo.data.Store} store
28061  */
28062 Roo.bootstrap.PagingToolbar = function(config)
28063 {
28064     // old args format still supported... - xtype is prefered..
28065         // created from xtype...
28066     
28067     this.ds = config.dataSource;
28068     
28069     if (config.store && !this.ds) {
28070         this.store= Roo.factory(config.store, Roo.data);
28071         this.ds = this.store;
28072         this.ds.xmodule = this.xmodule || false;
28073     }
28074     
28075     this.toolbarItems = [];
28076     if (config.items) {
28077         this.toolbarItems = config.items;
28078     }
28079     
28080     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28081     
28082     this.cursor = 0;
28083     
28084     if (this.ds) { 
28085         this.bind(this.ds);
28086     }
28087     
28088     if (Roo.bootstrap.version == 4) {
28089         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28090     } else {
28091         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28092     }
28093     
28094 };
28095
28096 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28097     /**
28098      * @cfg {Roo.data.Store} dataSource
28099      * The underlying data store providing the paged data
28100      */
28101     /**
28102      * @cfg {String/HTMLElement/Element} container
28103      * container The id or element that will contain the toolbar
28104      */
28105     /**
28106      * @cfg {Boolean} displayInfo
28107      * True to display the displayMsg (defaults to false)
28108      */
28109     /**
28110      * @cfg {Number} pageSize
28111      * The number of records to display per page (defaults to 20)
28112      */
28113     pageSize: 20,
28114     /**
28115      * @cfg {String} displayMsg
28116      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28117      */
28118     displayMsg : 'Displaying {0} - {1} of {2}',
28119     /**
28120      * @cfg {String} emptyMsg
28121      * The message to display when no records are found (defaults to "No data to display")
28122      */
28123     emptyMsg : 'No data to display',
28124     /**
28125      * Customizable piece of the default paging text (defaults to "Page")
28126      * @type String
28127      */
28128     beforePageText : "Page",
28129     /**
28130      * Customizable piece of the default paging text (defaults to "of %0")
28131      * @type String
28132      */
28133     afterPageText : "of {0}",
28134     /**
28135      * Customizable piece of the default paging text (defaults to "First Page")
28136      * @type String
28137      */
28138     firstText : "First Page",
28139     /**
28140      * Customizable piece of the default paging text (defaults to "Previous Page")
28141      * @type String
28142      */
28143     prevText : "Previous Page",
28144     /**
28145      * Customizable piece of the default paging text (defaults to "Next Page")
28146      * @type String
28147      */
28148     nextText : "Next Page",
28149     /**
28150      * Customizable piece of the default paging text (defaults to "Last Page")
28151      * @type String
28152      */
28153     lastText : "Last Page",
28154     /**
28155      * Customizable piece of the default paging text (defaults to "Refresh")
28156      * @type String
28157      */
28158     refreshText : "Refresh",
28159
28160     buttons : false,
28161     // private
28162     onRender : function(ct, position) 
28163     {
28164         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28165         this.navgroup.parentId = this.id;
28166         this.navgroup.onRender(this.el, null);
28167         // add the buttons to the navgroup
28168         
28169         if(this.displayInfo){
28170             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28171             this.displayEl = this.el.select('.x-paging-info', true).first();
28172 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28173 //            this.displayEl = navel.el.select('span',true).first();
28174         }
28175         
28176         var _this = this;
28177         
28178         if(this.buttons){
28179             Roo.each(_this.buttons, function(e){ // this might need to use render????
28180                Roo.factory(e).render(_this.el);
28181             });
28182         }
28183             
28184         Roo.each(_this.toolbarItems, function(e) {
28185             _this.navgroup.addItem(e);
28186         });
28187         
28188         
28189         this.first = this.navgroup.addItem({
28190             tooltip: this.firstText,
28191             cls: "prev btn-outline-secondary",
28192             html : ' <i class="fa fa-step-backward"></i>',
28193             disabled: true,
28194             preventDefault: true,
28195             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28196         });
28197         
28198         this.prev =  this.navgroup.addItem({
28199             tooltip: this.prevText,
28200             cls: "prev btn-outline-secondary",
28201             html : ' <i class="fa fa-backward"></i>',
28202             disabled: true,
28203             preventDefault: true,
28204             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28205         });
28206     //this.addSeparator();
28207         
28208         
28209         var field = this.navgroup.addItem( {
28210             tagtype : 'span',
28211             cls : 'x-paging-position  btn-outline-secondary',
28212              disabled: true,
28213             html : this.beforePageText  +
28214                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28215                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28216          } ); //?? escaped?
28217         
28218         this.field = field.el.select('input', true).first();
28219         this.field.on("keydown", this.onPagingKeydown, this);
28220         this.field.on("focus", function(){this.dom.select();});
28221     
28222     
28223         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28224         //this.field.setHeight(18);
28225         //this.addSeparator();
28226         this.next = this.navgroup.addItem({
28227             tooltip: this.nextText,
28228             cls: "next btn-outline-secondary",
28229             html : ' <i class="fa fa-forward"></i>',
28230             disabled: true,
28231             preventDefault: true,
28232             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28233         });
28234         this.last = this.navgroup.addItem({
28235             tooltip: this.lastText,
28236             html : ' <i class="fa fa-step-forward"></i>',
28237             cls: "next btn-outline-secondary",
28238             disabled: true,
28239             preventDefault: true,
28240             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28241         });
28242     //this.addSeparator();
28243         this.loading = this.navgroup.addItem({
28244             tooltip: this.refreshText,
28245             cls: "btn-outline-secondary",
28246             html : ' <i class="fa fa-refresh"></i>',
28247             preventDefault: true,
28248             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28249         });
28250         
28251     },
28252
28253     // private
28254     updateInfo : function(){
28255         if(this.displayEl){
28256             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28257             var msg = count == 0 ?
28258                 this.emptyMsg :
28259                 String.format(
28260                     this.displayMsg,
28261                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28262                 );
28263             this.displayEl.update(msg);
28264         }
28265     },
28266
28267     // private
28268     onLoad : function(ds, r, o)
28269     {
28270         this.cursor = o.params && o.params.start ? o.params.start : 0;
28271         
28272         var d = this.getPageData(),
28273             ap = d.activePage,
28274             ps = d.pages;
28275         
28276         
28277         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28278         this.field.dom.value = ap;
28279         this.first.setDisabled(ap == 1);
28280         this.prev.setDisabled(ap == 1);
28281         this.next.setDisabled(ap == ps);
28282         this.last.setDisabled(ap == ps);
28283         this.loading.enable();
28284         this.updateInfo();
28285     },
28286
28287     // private
28288     getPageData : function(){
28289         var total = this.ds.getTotalCount();
28290         return {
28291             total : total,
28292             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28293             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28294         };
28295     },
28296
28297     // private
28298     onLoadError : function(){
28299         this.loading.enable();
28300     },
28301
28302     // private
28303     onPagingKeydown : function(e){
28304         var k = e.getKey();
28305         var d = this.getPageData();
28306         if(k == e.RETURN){
28307             var v = this.field.dom.value, pageNum;
28308             if(!v || isNaN(pageNum = parseInt(v, 10))){
28309                 this.field.dom.value = d.activePage;
28310                 return;
28311             }
28312             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28313             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28314             e.stopEvent();
28315         }
28316         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))
28317         {
28318           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28319           this.field.dom.value = pageNum;
28320           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28321           e.stopEvent();
28322         }
28323         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28324         {
28325           var v = this.field.dom.value, pageNum; 
28326           var increment = (e.shiftKey) ? 10 : 1;
28327           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28328                 increment *= -1;
28329           }
28330           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28331             this.field.dom.value = d.activePage;
28332             return;
28333           }
28334           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28335           {
28336             this.field.dom.value = parseInt(v, 10) + increment;
28337             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28338             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28339           }
28340           e.stopEvent();
28341         }
28342     },
28343
28344     // private
28345     beforeLoad : function(){
28346         if(this.loading){
28347             this.loading.disable();
28348         }
28349     },
28350
28351     // private
28352     onClick : function(which){
28353         
28354         var ds = this.ds;
28355         if (!ds) {
28356             return;
28357         }
28358         
28359         switch(which){
28360             case "first":
28361                 ds.load({params:{start: 0, limit: this.pageSize}});
28362             break;
28363             case "prev":
28364                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28365             break;
28366             case "next":
28367                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28368             break;
28369             case "last":
28370                 var total = ds.getTotalCount();
28371                 var extra = total % this.pageSize;
28372                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28373                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28374             break;
28375             case "refresh":
28376                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28377             break;
28378         }
28379     },
28380
28381     /**
28382      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28383      * @param {Roo.data.Store} store The data store to unbind
28384      */
28385     unbind : function(ds){
28386         ds.un("beforeload", this.beforeLoad, this);
28387         ds.un("load", this.onLoad, this);
28388         ds.un("loadexception", this.onLoadError, this);
28389         ds.un("remove", this.updateInfo, this);
28390         ds.un("add", this.updateInfo, this);
28391         this.ds = undefined;
28392     },
28393
28394     /**
28395      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28396      * @param {Roo.data.Store} store The data store to bind
28397      */
28398     bind : function(ds){
28399         ds.on("beforeload", this.beforeLoad, this);
28400         ds.on("load", this.onLoad, this);
28401         ds.on("loadexception", this.onLoadError, this);
28402         ds.on("remove", this.updateInfo, this);
28403         ds.on("add", this.updateInfo, this);
28404         this.ds = ds;
28405     }
28406 });/*
28407  * - LGPL
28408  *
28409  * element
28410  * 
28411  */
28412
28413 /**
28414  * @class Roo.bootstrap.MessageBar
28415  * @extends Roo.bootstrap.Component
28416  * Bootstrap MessageBar class
28417  * @cfg {String} html contents of the MessageBar
28418  * @cfg {String} weight (info | success | warning | danger) default info
28419  * @cfg {String} beforeClass insert the bar before the given class
28420  * @cfg {Boolean} closable (true | false) default false
28421  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28422  * 
28423  * @constructor
28424  * Create a new Element
28425  * @param {Object} config The config object
28426  */
28427
28428 Roo.bootstrap.MessageBar = function(config){
28429     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28430 };
28431
28432 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28433     
28434     html: '',
28435     weight: 'info',
28436     closable: false,
28437     fixed: false,
28438     beforeClass: 'bootstrap-sticky-wrap',
28439     
28440     getAutoCreate : function(){
28441         
28442         var cfg = {
28443             tag: 'div',
28444             cls: 'alert alert-dismissable alert-' + this.weight,
28445             cn: [
28446                 {
28447                     tag: 'span',
28448                     cls: 'message',
28449                     html: this.html || ''
28450                 }
28451             ]
28452         };
28453         
28454         if(this.fixed){
28455             cfg.cls += ' alert-messages-fixed';
28456         }
28457         
28458         if(this.closable){
28459             cfg.cn.push({
28460                 tag: 'button',
28461                 cls: 'close',
28462                 html: 'x'
28463             });
28464         }
28465         
28466         return cfg;
28467     },
28468     
28469     onRender : function(ct, position)
28470     {
28471         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28472         
28473         if(!this.el){
28474             var cfg = Roo.apply({},  this.getAutoCreate());
28475             cfg.id = Roo.id();
28476             
28477             if (this.cls) {
28478                 cfg.cls += ' ' + this.cls;
28479             }
28480             if (this.style) {
28481                 cfg.style = this.style;
28482             }
28483             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28484             
28485             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28486         }
28487         
28488         this.el.select('>button.close').on('click', this.hide, this);
28489         
28490     },
28491     
28492     show : function()
28493     {
28494         if (!this.rendered) {
28495             this.render();
28496         }
28497         
28498         this.el.show();
28499         
28500         this.fireEvent('show', this);
28501         
28502     },
28503     
28504     hide : function()
28505     {
28506         if (!this.rendered) {
28507             this.render();
28508         }
28509         
28510         this.el.hide();
28511         
28512         this.fireEvent('hide', this);
28513     },
28514     
28515     update : function()
28516     {
28517 //        var e = this.el.dom.firstChild;
28518 //        
28519 //        if(this.closable){
28520 //            e = e.nextSibling;
28521 //        }
28522 //        
28523 //        e.data = this.html || '';
28524
28525         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28526     }
28527    
28528 });
28529
28530  
28531
28532      /*
28533  * - LGPL
28534  *
28535  * Graph
28536  * 
28537  */
28538
28539
28540 /**
28541  * @class Roo.bootstrap.Graph
28542  * @extends Roo.bootstrap.Component
28543  * Bootstrap Graph class
28544 > Prameters
28545  -sm {number} sm 4
28546  -md {number} md 5
28547  @cfg {String} graphtype  bar | vbar | pie
28548  @cfg {number} g_x coodinator | centre x (pie)
28549  @cfg {number} g_y coodinator | centre y (pie)
28550  @cfg {number} g_r radius (pie)
28551  @cfg {number} g_height height of the chart (respected by all elements in the set)
28552  @cfg {number} g_width width of the chart (respected by all elements in the set)
28553  @cfg {Object} title The title of the chart
28554     
28555  -{Array}  values
28556  -opts (object) options for the chart 
28557      o {
28558      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28559      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28560      o vgutter (number)
28561      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.
28562      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28563      o to
28564      o stretch (boolean)
28565      o }
28566  -opts (object) options for the pie
28567      o{
28568      o cut
28569      o startAngle (number)
28570      o endAngle (number)
28571      } 
28572  *
28573  * @constructor
28574  * Create a new Input
28575  * @param {Object} config The config object
28576  */
28577
28578 Roo.bootstrap.Graph = function(config){
28579     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28580     
28581     this.addEvents({
28582         // img events
28583         /**
28584          * @event click
28585          * The img click event for the img.
28586          * @param {Roo.EventObject} e
28587          */
28588         "click" : true
28589     });
28590 };
28591
28592 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28593     
28594     sm: 4,
28595     md: 5,
28596     graphtype: 'bar',
28597     g_height: 250,
28598     g_width: 400,
28599     g_x: 50,
28600     g_y: 50,
28601     g_r: 30,
28602     opts:{
28603         //g_colors: this.colors,
28604         g_type: 'soft',
28605         g_gutter: '20%'
28606
28607     },
28608     title : false,
28609
28610     getAutoCreate : function(){
28611         
28612         var cfg = {
28613             tag: 'div',
28614             html : null
28615         };
28616         
28617         
28618         return  cfg;
28619     },
28620
28621     onRender : function(ct,position){
28622         
28623         
28624         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28625         
28626         if (typeof(Raphael) == 'undefined') {
28627             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28628             return;
28629         }
28630         
28631         this.raphael = Raphael(this.el.dom);
28632         
28633                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28634                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28635                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28636                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28637                 /*
28638                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28639                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28640                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28641                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28642                 
28643                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28644                 r.barchart(330, 10, 300, 220, data1);
28645                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28646                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28647                 */
28648                 
28649                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28650                 // r.barchart(30, 30, 560, 250,  xdata, {
28651                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28652                 //     axis : "0 0 1 1",
28653                 //     axisxlabels :  xdata
28654                 //     //yvalues : cols,
28655                    
28656                 // });
28657 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28658 //        
28659 //        this.load(null,xdata,{
28660 //                axis : "0 0 1 1",
28661 //                axisxlabels :  xdata
28662 //                });
28663
28664     },
28665
28666     load : function(graphtype,xdata,opts)
28667     {
28668         this.raphael.clear();
28669         if(!graphtype) {
28670             graphtype = this.graphtype;
28671         }
28672         if(!opts){
28673             opts = this.opts;
28674         }
28675         var r = this.raphael,
28676             fin = function () {
28677                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28678             },
28679             fout = function () {
28680                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28681             },
28682             pfin = function() {
28683                 this.sector.stop();
28684                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28685
28686                 if (this.label) {
28687                     this.label[0].stop();
28688                     this.label[0].attr({ r: 7.5 });
28689                     this.label[1].attr({ "font-weight": 800 });
28690                 }
28691             },
28692             pfout = function() {
28693                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28694
28695                 if (this.label) {
28696                     this.label[0].animate({ r: 5 }, 500, "bounce");
28697                     this.label[1].attr({ "font-weight": 400 });
28698                 }
28699             };
28700
28701         switch(graphtype){
28702             case 'bar':
28703                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28704                 break;
28705             case 'hbar':
28706                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28707                 break;
28708             case 'pie':
28709 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28710 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28711 //            
28712                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28713                 
28714                 break;
28715
28716         }
28717         
28718         if(this.title){
28719             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28720         }
28721         
28722     },
28723     
28724     setTitle: function(o)
28725     {
28726         this.title = o;
28727     },
28728     
28729     initEvents: function() {
28730         
28731         if(!this.href){
28732             this.el.on('click', this.onClick, this);
28733         }
28734     },
28735     
28736     onClick : function(e)
28737     {
28738         Roo.log('img onclick');
28739         this.fireEvent('click', this, e);
28740     }
28741    
28742 });
28743
28744  
28745 /*
28746  * - LGPL
28747  *
28748  * numberBox
28749  * 
28750  */
28751 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28752
28753 /**
28754  * @class Roo.bootstrap.dash.NumberBox
28755  * @extends Roo.bootstrap.Component
28756  * Bootstrap NumberBox class
28757  * @cfg {String} headline Box headline
28758  * @cfg {String} content Box content
28759  * @cfg {String} icon Box icon
28760  * @cfg {String} footer Footer text
28761  * @cfg {String} fhref Footer href
28762  * 
28763  * @constructor
28764  * Create a new NumberBox
28765  * @param {Object} config The config object
28766  */
28767
28768
28769 Roo.bootstrap.dash.NumberBox = function(config){
28770     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28771     
28772 };
28773
28774 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28775     
28776     headline : '',
28777     content : '',
28778     icon : '',
28779     footer : '',
28780     fhref : '',
28781     ficon : '',
28782     
28783     getAutoCreate : function(){
28784         
28785         var cfg = {
28786             tag : 'div',
28787             cls : 'small-box ',
28788             cn : [
28789                 {
28790                     tag : 'div',
28791                     cls : 'inner',
28792                     cn :[
28793                         {
28794                             tag : 'h3',
28795                             cls : 'roo-headline',
28796                             html : this.headline
28797                         },
28798                         {
28799                             tag : 'p',
28800                             cls : 'roo-content',
28801                             html : this.content
28802                         }
28803                     ]
28804                 }
28805             ]
28806         };
28807         
28808         if(this.icon){
28809             cfg.cn.push({
28810                 tag : 'div',
28811                 cls : 'icon',
28812                 cn :[
28813                     {
28814                         tag : 'i',
28815                         cls : 'ion ' + this.icon
28816                     }
28817                 ]
28818             });
28819         }
28820         
28821         if(this.footer){
28822             var footer = {
28823                 tag : 'a',
28824                 cls : 'small-box-footer',
28825                 href : this.fhref || '#',
28826                 html : this.footer
28827             };
28828             
28829             cfg.cn.push(footer);
28830             
28831         }
28832         
28833         return  cfg;
28834     },
28835
28836     onRender : function(ct,position){
28837         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28838
28839
28840        
28841                 
28842     },
28843
28844     setHeadline: function (value)
28845     {
28846         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28847     },
28848     
28849     setFooter: function (value, href)
28850     {
28851         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28852         
28853         if(href){
28854             this.el.select('a.small-box-footer',true).first().attr('href', href);
28855         }
28856         
28857     },
28858
28859     setContent: function (value)
28860     {
28861         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28862     },
28863
28864     initEvents: function() 
28865     {   
28866         
28867     }
28868     
28869 });
28870
28871  
28872 /*
28873  * - LGPL
28874  *
28875  * TabBox
28876  * 
28877  */
28878 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28879
28880 /**
28881  * @class Roo.bootstrap.dash.TabBox
28882  * @extends Roo.bootstrap.Component
28883  * Bootstrap TabBox class
28884  * @cfg {String} title Title of the TabBox
28885  * @cfg {String} icon Icon of the TabBox
28886  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28887  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28888  * 
28889  * @constructor
28890  * Create a new TabBox
28891  * @param {Object} config The config object
28892  */
28893
28894
28895 Roo.bootstrap.dash.TabBox = function(config){
28896     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28897     this.addEvents({
28898         // raw events
28899         /**
28900          * @event addpane
28901          * When a pane is added
28902          * @param {Roo.bootstrap.dash.TabPane} pane
28903          */
28904         "addpane" : true,
28905         /**
28906          * @event activatepane
28907          * When a pane is activated
28908          * @param {Roo.bootstrap.dash.TabPane} pane
28909          */
28910         "activatepane" : true
28911         
28912          
28913     });
28914     
28915     this.panes = [];
28916 };
28917
28918 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28919
28920     title : '',
28921     icon : false,
28922     showtabs : true,
28923     tabScrollable : false,
28924     
28925     getChildContainer : function()
28926     {
28927         return this.el.select('.tab-content', true).first();
28928     },
28929     
28930     getAutoCreate : function(){
28931         
28932         var header = {
28933             tag: 'li',
28934             cls: 'pull-left header',
28935             html: this.title,
28936             cn : []
28937         };
28938         
28939         if(this.icon){
28940             header.cn.push({
28941                 tag: 'i',
28942                 cls: 'fa ' + this.icon
28943             });
28944         }
28945         
28946         var h = {
28947             tag: 'ul',
28948             cls: 'nav nav-tabs pull-right',
28949             cn: [
28950                 header
28951             ]
28952         };
28953         
28954         if(this.tabScrollable){
28955             h = {
28956                 tag: 'div',
28957                 cls: 'tab-header',
28958                 cn: [
28959                     {
28960                         tag: 'ul',
28961                         cls: 'nav nav-tabs pull-right',
28962                         cn: [
28963                             header
28964                         ]
28965                     }
28966                 ]
28967             };
28968         }
28969         
28970         var cfg = {
28971             tag: 'div',
28972             cls: 'nav-tabs-custom',
28973             cn: [
28974                 h,
28975                 {
28976                     tag: 'div',
28977                     cls: 'tab-content no-padding',
28978                     cn: []
28979                 }
28980             ]
28981         };
28982
28983         return  cfg;
28984     },
28985     initEvents : function()
28986     {
28987         //Roo.log('add add pane handler');
28988         this.on('addpane', this.onAddPane, this);
28989     },
28990      /**
28991      * Updates the box title
28992      * @param {String} html to set the title to.
28993      */
28994     setTitle : function(value)
28995     {
28996         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28997     },
28998     onAddPane : function(pane)
28999     {
29000         this.panes.push(pane);
29001         //Roo.log('addpane');
29002         //Roo.log(pane);
29003         // tabs are rendere left to right..
29004         if(!this.showtabs){
29005             return;
29006         }
29007         
29008         var ctr = this.el.select('.nav-tabs', true).first();
29009          
29010          
29011         var existing = ctr.select('.nav-tab',true);
29012         var qty = existing.getCount();;
29013         
29014         
29015         var tab = ctr.createChild({
29016             tag : 'li',
29017             cls : 'nav-tab' + (qty ? '' : ' active'),
29018             cn : [
29019                 {
29020                     tag : 'a',
29021                     href:'#',
29022                     html : pane.title
29023                 }
29024             ]
29025         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29026         pane.tab = tab;
29027         
29028         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29029         if (!qty) {
29030             pane.el.addClass('active');
29031         }
29032         
29033                 
29034     },
29035     onTabClick : function(ev,un,ob,pane)
29036     {
29037         //Roo.log('tab - prev default');
29038         ev.preventDefault();
29039         
29040         
29041         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29042         pane.tab.addClass('active');
29043         //Roo.log(pane.title);
29044         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29045         // technically we should have a deactivate event.. but maybe add later.
29046         // and it should not de-activate the selected tab...
29047         this.fireEvent('activatepane', pane);
29048         pane.el.addClass('active');
29049         pane.fireEvent('activate');
29050         
29051         
29052     },
29053     
29054     getActivePane : function()
29055     {
29056         var r = false;
29057         Roo.each(this.panes, function(p) {
29058             if(p.el.hasClass('active')){
29059                 r = p;
29060                 return false;
29061             }
29062             
29063             return;
29064         });
29065         
29066         return r;
29067     }
29068     
29069     
29070 });
29071
29072  
29073 /*
29074  * - LGPL
29075  *
29076  * Tab pane
29077  * 
29078  */
29079 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29080 /**
29081  * @class Roo.bootstrap.TabPane
29082  * @extends Roo.bootstrap.Component
29083  * Bootstrap TabPane class
29084  * @cfg {Boolean} active (false | true) Default false
29085  * @cfg {String} title title of panel
29086
29087  * 
29088  * @constructor
29089  * Create a new TabPane
29090  * @param {Object} config The config object
29091  */
29092
29093 Roo.bootstrap.dash.TabPane = function(config){
29094     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29095     
29096     this.addEvents({
29097         // raw events
29098         /**
29099          * @event activate
29100          * When a pane is activated
29101          * @param {Roo.bootstrap.dash.TabPane} pane
29102          */
29103         "activate" : true
29104          
29105     });
29106 };
29107
29108 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29109     
29110     active : false,
29111     title : '',
29112     
29113     // the tabBox that this is attached to.
29114     tab : false,
29115      
29116     getAutoCreate : function() 
29117     {
29118         var cfg = {
29119             tag: 'div',
29120             cls: 'tab-pane'
29121         };
29122         
29123         if(this.active){
29124             cfg.cls += ' active';
29125         }
29126         
29127         return cfg;
29128     },
29129     initEvents  : function()
29130     {
29131         //Roo.log('trigger add pane handler');
29132         this.parent().fireEvent('addpane', this)
29133     },
29134     
29135      /**
29136      * Updates the tab title 
29137      * @param {String} html to set the title to.
29138      */
29139     setTitle: function(str)
29140     {
29141         if (!this.tab) {
29142             return;
29143         }
29144         this.title = str;
29145         this.tab.select('a', true).first().dom.innerHTML = str;
29146         
29147     }
29148     
29149     
29150     
29151 });
29152
29153  
29154
29155
29156  /*
29157  * - LGPL
29158  *
29159  * menu
29160  * 
29161  */
29162 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29163
29164 /**
29165  * @class Roo.bootstrap.menu.Menu
29166  * @extends Roo.bootstrap.Component
29167  * Bootstrap Menu class - container for Menu
29168  * @cfg {String} html Text of the menu
29169  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29170  * @cfg {String} icon Font awesome icon
29171  * @cfg {String} pos Menu align to (top | bottom) default bottom
29172  * 
29173  * 
29174  * @constructor
29175  * Create a new Menu
29176  * @param {Object} config The config object
29177  */
29178
29179
29180 Roo.bootstrap.menu.Menu = function(config){
29181     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29182     
29183     this.addEvents({
29184         /**
29185          * @event beforeshow
29186          * Fires before this menu is displayed
29187          * @param {Roo.bootstrap.menu.Menu} this
29188          */
29189         beforeshow : true,
29190         /**
29191          * @event beforehide
29192          * Fires before this menu is hidden
29193          * @param {Roo.bootstrap.menu.Menu} this
29194          */
29195         beforehide : true,
29196         /**
29197          * @event show
29198          * Fires after this menu is displayed
29199          * @param {Roo.bootstrap.menu.Menu} this
29200          */
29201         show : true,
29202         /**
29203          * @event hide
29204          * Fires after this menu is hidden
29205          * @param {Roo.bootstrap.menu.Menu} this
29206          */
29207         hide : true,
29208         /**
29209          * @event click
29210          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29211          * @param {Roo.bootstrap.menu.Menu} this
29212          * @param {Roo.EventObject} e
29213          */
29214         click : true
29215     });
29216     
29217 };
29218
29219 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29220     
29221     submenu : false,
29222     html : '',
29223     weight : 'default',
29224     icon : false,
29225     pos : 'bottom',
29226     
29227     
29228     getChildContainer : function() {
29229         if(this.isSubMenu){
29230             return this.el;
29231         }
29232         
29233         return this.el.select('ul.dropdown-menu', true).first();  
29234     },
29235     
29236     getAutoCreate : function()
29237     {
29238         var text = [
29239             {
29240                 tag : 'span',
29241                 cls : 'roo-menu-text',
29242                 html : this.html
29243             }
29244         ];
29245         
29246         if(this.icon){
29247             text.unshift({
29248                 tag : 'i',
29249                 cls : 'fa ' + this.icon
29250             })
29251         }
29252         
29253         
29254         var cfg = {
29255             tag : 'div',
29256             cls : 'btn-group',
29257             cn : [
29258                 {
29259                     tag : 'button',
29260                     cls : 'dropdown-button btn btn-' + this.weight,
29261                     cn : text
29262                 },
29263                 {
29264                     tag : 'button',
29265                     cls : 'dropdown-toggle btn btn-' + this.weight,
29266                     cn : [
29267                         {
29268                             tag : 'span',
29269                             cls : 'caret'
29270                         }
29271                     ]
29272                 },
29273                 {
29274                     tag : 'ul',
29275                     cls : 'dropdown-menu'
29276                 }
29277             ]
29278             
29279         };
29280         
29281         if(this.pos == 'top'){
29282             cfg.cls += ' dropup';
29283         }
29284         
29285         if(this.isSubMenu){
29286             cfg = {
29287                 tag : 'ul',
29288                 cls : 'dropdown-menu'
29289             }
29290         }
29291         
29292         return cfg;
29293     },
29294     
29295     onRender : function(ct, position)
29296     {
29297         this.isSubMenu = ct.hasClass('dropdown-submenu');
29298         
29299         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29300     },
29301     
29302     initEvents : function() 
29303     {
29304         if(this.isSubMenu){
29305             return;
29306         }
29307         
29308         this.hidden = true;
29309         
29310         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29311         this.triggerEl.on('click', this.onTriggerPress, this);
29312         
29313         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29314         this.buttonEl.on('click', this.onClick, this);
29315         
29316     },
29317     
29318     list : function()
29319     {
29320         if(this.isSubMenu){
29321             return this.el;
29322         }
29323         
29324         return this.el.select('ul.dropdown-menu', true).first();
29325     },
29326     
29327     onClick : function(e)
29328     {
29329         this.fireEvent("click", this, e);
29330     },
29331     
29332     onTriggerPress  : function(e)
29333     {   
29334         if (this.isVisible()) {
29335             this.hide();
29336         } else {
29337             this.show();
29338         }
29339     },
29340     
29341     isVisible : function(){
29342         return !this.hidden;
29343     },
29344     
29345     show : function()
29346     {
29347         this.fireEvent("beforeshow", this);
29348         
29349         this.hidden = false;
29350         this.el.addClass('open');
29351         
29352         Roo.get(document).on("mouseup", this.onMouseUp, this);
29353         
29354         this.fireEvent("show", this);
29355         
29356         
29357     },
29358     
29359     hide : function()
29360     {
29361         this.fireEvent("beforehide", this);
29362         
29363         this.hidden = true;
29364         this.el.removeClass('open');
29365         
29366         Roo.get(document).un("mouseup", this.onMouseUp);
29367         
29368         this.fireEvent("hide", this);
29369     },
29370     
29371     onMouseUp : function()
29372     {
29373         this.hide();
29374     }
29375     
29376 });
29377
29378  
29379  /*
29380  * - LGPL
29381  *
29382  * menu item
29383  * 
29384  */
29385 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29386
29387 /**
29388  * @class Roo.bootstrap.menu.Item
29389  * @extends Roo.bootstrap.Component
29390  * Bootstrap MenuItem class
29391  * @cfg {Boolean} submenu (true | false) default false
29392  * @cfg {String} html text of the item
29393  * @cfg {String} href the link
29394  * @cfg {Boolean} disable (true | false) default false
29395  * @cfg {Boolean} preventDefault (true | false) default true
29396  * @cfg {String} icon Font awesome icon
29397  * @cfg {String} pos Submenu align to (left | right) default right 
29398  * 
29399  * 
29400  * @constructor
29401  * Create a new Item
29402  * @param {Object} config The config object
29403  */
29404
29405
29406 Roo.bootstrap.menu.Item = function(config){
29407     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29408     this.addEvents({
29409         /**
29410          * @event mouseover
29411          * Fires when the mouse is hovering over this menu
29412          * @param {Roo.bootstrap.menu.Item} this
29413          * @param {Roo.EventObject} e
29414          */
29415         mouseover : true,
29416         /**
29417          * @event mouseout
29418          * Fires when the mouse exits this menu
29419          * @param {Roo.bootstrap.menu.Item} this
29420          * @param {Roo.EventObject} e
29421          */
29422         mouseout : true,
29423         // raw events
29424         /**
29425          * @event click
29426          * The raw click event for the entire grid.
29427          * @param {Roo.EventObject} e
29428          */
29429         click : true
29430     });
29431 };
29432
29433 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29434     
29435     submenu : false,
29436     href : '',
29437     html : '',
29438     preventDefault: true,
29439     disable : false,
29440     icon : false,
29441     pos : 'right',
29442     
29443     getAutoCreate : function()
29444     {
29445         var text = [
29446             {
29447                 tag : 'span',
29448                 cls : 'roo-menu-item-text',
29449                 html : this.html
29450             }
29451         ];
29452         
29453         if(this.icon){
29454             text.unshift({
29455                 tag : 'i',
29456                 cls : 'fa ' + this.icon
29457             })
29458         }
29459         
29460         var cfg = {
29461             tag : 'li',
29462             cn : [
29463                 {
29464                     tag : 'a',
29465                     href : this.href || '#',
29466                     cn : text
29467                 }
29468             ]
29469         };
29470         
29471         if(this.disable){
29472             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29473         }
29474         
29475         if(this.submenu){
29476             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29477             
29478             if(this.pos == 'left'){
29479                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29480             }
29481         }
29482         
29483         return cfg;
29484     },
29485     
29486     initEvents : function() 
29487     {
29488         this.el.on('mouseover', this.onMouseOver, this);
29489         this.el.on('mouseout', this.onMouseOut, this);
29490         
29491         this.el.select('a', true).first().on('click', this.onClick, this);
29492         
29493     },
29494     
29495     onClick : function(e)
29496     {
29497         if(this.preventDefault){
29498             e.preventDefault();
29499         }
29500         
29501         this.fireEvent("click", this, e);
29502     },
29503     
29504     onMouseOver : function(e)
29505     {
29506         if(this.submenu && this.pos == 'left'){
29507             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29508         }
29509         
29510         this.fireEvent("mouseover", this, e);
29511     },
29512     
29513     onMouseOut : function(e)
29514     {
29515         this.fireEvent("mouseout", this, e);
29516     }
29517 });
29518
29519  
29520
29521  /*
29522  * - LGPL
29523  *
29524  * menu separator
29525  * 
29526  */
29527 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29528
29529 /**
29530  * @class Roo.bootstrap.menu.Separator
29531  * @extends Roo.bootstrap.Component
29532  * Bootstrap Separator class
29533  * 
29534  * @constructor
29535  * Create a new Separator
29536  * @param {Object} config The config object
29537  */
29538
29539
29540 Roo.bootstrap.menu.Separator = function(config){
29541     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29542 };
29543
29544 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29545     
29546     getAutoCreate : function(){
29547         var cfg = {
29548             tag : 'li',
29549             cls: 'dropdown-divider divider'
29550         };
29551         
29552         return cfg;
29553     }
29554    
29555 });
29556
29557  
29558
29559  /*
29560  * - LGPL
29561  *
29562  * Tooltip
29563  * 
29564  */
29565
29566 /**
29567  * @class Roo.bootstrap.Tooltip
29568  * Bootstrap Tooltip class
29569  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29570  * to determine which dom element triggers the tooltip.
29571  * 
29572  * It needs to add support for additional attributes like tooltip-position
29573  * 
29574  * @constructor
29575  * Create a new Toolti
29576  * @param {Object} config The config object
29577  */
29578
29579 Roo.bootstrap.Tooltip = function(config){
29580     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29581     
29582     this.alignment = Roo.bootstrap.Tooltip.alignment;
29583     
29584     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29585         this.alignment = config.alignment;
29586     }
29587     
29588 };
29589
29590 Roo.apply(Roo.bootstrap.Tooltip, {
29591     /**
29592      * @function init initialize tooltip monitoring.
29593      * @static
29594      */
29595     currentEl : false,
29596     currentTip : false,
29597     currentRegion : false,
29598     
29599     //  init : delay?
29600     
29601     init : function()
29602     {
29603         Roo.get(document).on('mouseover', this.enter ,this);
29604         Roo.get(document).on('mouseout', this.leave, this);
29605          
29606         
29607         this.currentTip = new Roo.bootstrap.Tooltip();
29608     },
29609     
29610     enter : function(ev)
29611     {
29612         var dom = ev.getTarget();
29613         
29614         //Roo.log(['enter',dom]);
29615         var el = Roo.fly(dom);
29616         if (this.currentEl) {
29617             //Roo.log(dom);
29618             //Roo.log(this.currentEl);
29619             //Roo.log(this.currentEl.contains(dom));
29620             if (this.currentEl == el) {
29621                 return;
29622             }
29623             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29624                 return;
29625             }
29626
29627         }
29628         
29629         if (this.currentTip.el) {
29630             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29631         }    
29632         //Roo.log(ev);
29633         
29634         if(!el || el.dom == document){
29635             return;
29636         }
29637         
29638         var bindEl = el; 
29639         var pel = false;
29640         if (!el.attr('tooltip')) {
29641             pel = el.findParent("[tooltip]");
29642             if (pel) {
29643                 bindEl = Roo.get(pel);
29644             }
29645         }
29646         
29647        
29648         
29649         // you can not look for children, as if el is the body.. then everythign is the child..
29650         if (!pel && !el.attr('tooltip')) { //
29651             if (!el.select("[tooltip]").elements.length) {
29652                 return;
29653             }
29654             // is the mouse over this child...?
29655             bindEl = el.select("[tooltip]").first();
29656             var xy = ev.getXY();
29657             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29658                 //Roo.log("not in region.");
29659                 return;
29660             }
29661             //Roo.log("child element over..");
29662             
29663         }
29664         this.currentEl = el;
29665         this.currentTip.bind(bindEl);
29666         this.currentRegion = Roo.lib.Region.getRegion(dom);
29667         this.currentTip.enter();
29668         
29669     },
29670     leave : function(ev)
29671     {
29672         var dom = ev.getTarget();
29673         //Roo.log(['leave',dom]);
29674         if (!this.currentEl) {
29675             return;
29676         }
29677         
29678         
29679         if (dom != this.currentEl.dom) {
29680             return;
29681         }
29682         var xy = ev.getXY();
29683         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29684             return;
29685         }
29686         // only activate leave if mouse cursor is outside... bounding box..
29687         
29688         
29689         
29690         
29691         if (this.currentTip) {
29692             this.currentTip.leave();
29693         }
29694         //Roo.log('clear currentEl');
29695         this.currentEl = false;
29696         
29697         
29698     },
29699     alignment : {
29700         'left' : ['r-l', [-2,0], 'right'],
29701         'right' : ['l-r', [2,0], 'left'],
29702         'bottom' : ['t-b', [0,2], 'top'],
29703         'top' : [ 'b-t', [0,-2], 'bottom']
29704     }
29705     
29706 });
29707
29708
29709 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29710     
29711     
29712     bindEl : false,
29713     
29714     delay : null, // can be { show : 300 , hide: 500}
29715     
29716     timeout : null,
29717     
29718     hoverState : null, //???
29719     
29720     placement : 'bottom', 
29721     
29722     alignment : false,
29723     
29724     getAutoCreate : function(){
29725     
29726         var cfg = {
29727            cls : 'tooltip',   
29728            role : 'tooltip',
29729            cn : [
29730                 {
29731                     cls : 'tooltip-arrow arrow'
29732                 },
29733                 {
29734                     cls : 'tooltip-inner'
29735                 }
29736            ]
29737         };
29738         
29739         return cfg;
29740     },
29741     bind : function(el)
29742     {
29743         this.bindEl = el;
29744     },
29745     
29746     initEvents : function()
29747     {
29748         this.arrowEl = this.el.select('.arrow', true).first();
29749         this.innerEl = this.el.select('.tooltip-inner', true).first();
29750     },
29751     
29752     enter : function () {
29753        
29754         if (this.timeout != null) {
29755             clearTimeout(this.timeout);
29756         }
29757         
29758         this.hoverState = 'in';
29759          //Roo.log("enter - show");
29760         if (!this.delay || !this.delay.show) {
29761             this.show();
29762             return;
29763         }
29764         var _t = this;
29765         this.timeout = setTimeout(function () {
29766             if (_t.hoverState == 'in') {
29767                 _t.show();
29768             }
29769         }, this.delay.show);
29770     },
29771     leave : function()
29772     {
29773         clearTimeout(this.timeout);
29774     
29775         this.hoverState = 'out';
29776          if (!this.delay || !this.delay.hide) {
29777             this.hide();
29778             return;
29779         }
29780        
29781         var _t = this;
29782         this.timeout = setTimeout(function () {
29783             //Roo.log("leave - timeout");
29784             
29785             if (_t.hoverState == 'out') {
29786                 _t.hide();
29787                 Roo.bootstrap.Tooltip.currentEl = false;
29788             }
29789         }, delay);
29790     },
29791     
29792     show : function (msg)
29793     {
29794         if (!this.el) {
29795             this.render(document.body);
29796         }
29797         // set content.
29798         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29799         
29800         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29801         
29802         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29803         
29804         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29805                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29806         
29807         var placement = typeof this.placement == 'function' ?
29808             this.placement.call(this, this.el, on_el) :
29809             this.placement;
29810             
29811         var autoToken = /\s?auto?\s?/i;
29812         var autoPlace = autoToken.test(placement);
29813         if (autoPlace) {
29814             placement = placement.replace(autoToken, '') || 'top';
29815         }
29816         
29817         //this.el.detach()
29818         //this.el.setXY([0,0]);
29819         this.el.show();
29820         //this.el.dom.style.display='block';
29821         
29822         //this.el.appendTo(on_el);
29823         
29824         var p = this.getPosition();
29825         var box = this.el.getBox();
29826         
29827         if (autoPlace) {
29828             // fixme..
29829         }
29830         
29831         var align = this.alignment[placement];
29832         
29833         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29834         
29835         if(placement == 'top' || placement == 'bottom'){
29836             if(xy[0] < 0){
29837                 placement = 'right';
29838             }
29839             
29840             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29841                 placement = 'left';
29842             }
29843             
29844             var scroll = Roo.select('body', true).first().getScroll();
29845             
29846             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29847                 placement = 'top';
29848             }
29849             
29850             align = this.alignment[placement];
29851             
29852             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29853             
29854         }
29855         
29856         var elems = document.getElementsByTagName('div');
29857         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29858         for (var i = 0; i < elems.length; i++) {
29859           var zindex = Number.parseInt(
29860                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29861                 10
29862           );
29863           if (zindex > highest) {
29864             highest = zindex;
29865           }
29866         }
29867         
29868         
29869         
29870         this.el.dom.style.zIndex = highest;
29871         
29872         this.el.alignTo(this.bindEl, align[0],align[1]);
29873         //var arrow = this.el.select('.arrow',true).first();
29874         //arrow.set(align[2], 
29875         
29876         this.el.addClass(placement);
29877         this.el.addClass("bs-tooltip-"+ placement);
29878         
29879         this.el.addClass('in fade show');
29880         
29881         this.hoverState = null;
29882         
29883         if (this.el.hasClass('fade')) {
29884             // fade it?
29885         }
29886         
29887         
29888         
29889         
29890         
29891     },
29892     hide : function()
29893     {
29894          
29895         if (!this.el) {
29896             return;
29897         }
29898         //this.el.setXY([0,0]);
29899         this.el.removeClass(['show', 'in']);
29900         //this.el.hide();
29901         
29902     }
29903     
29904 });
29905  
29906
29907  /*
29908  * - LGPL
29909  *
29910  * Location Picker
29911  * 
29912  */
29913
29914 /**
29915  * @class Roo.bootstrap.LocationPicker
29916  * @extends Roo.bootstrap.Component
29917  * Bootstrap LocationPicker class
29918  * @cfg {Number} latitude Position when init default 0
29919  * @cfg {Number} longitude Position when init default 0
29920  * @cfg {Number} zoom default 15
29921  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29922  * @cfg {Boolean} mapTypeControl default false
29923  * @cfg {Boolean} disableDoubleClickZoom default false
29924  * @cfg {Boolean} scrollwheel default true
29925  * @cfg {Boolean} streetViewControl default false
29926  * @cfg {Number} radius default 0
29927  * @cfg {String} locationName
29928  * @cfg {Boolean} draggable default true
29929  * @cfg {Boolean} enableAutocomplete default false
29930  * @cfg {Boolean} enableReverseGeocode default true
29931  * @cfg {String} markerTitle
29932  * 
29933  * @constructor
29934  * Create a new LocationPicker
29935  * @param {Object} config The config object
29936  */
29937
29938
29939 Roo.bootstrap.LocationPicker = function(config){
29940     
29941     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29942     
29943     this.addEvents({
29944         /**
29945          * @event initial
29946          * Fires when the picker initialized.
29947          * @param {Roo.bootstrap.LocationPicker} this
29948          * @param {Google Location} location
29949          */
29950         initial : true,
29951         /**
29952          * @event positionchanged
29953          * Fires when the picker position changed.
29954          * @param {Roo.bootstrap.LocationPicker} this
29955          * @param {Google Location} location
29956          */
29957         positionchanged : true,
29958         /**
29959          * @event resize
29960          * Fires when the map resize.
29961          * @param {Roo.bootstrap.LocationPicker} this
29962          */
29963         resize : true,
29964         /**
29965          * @event show
29966          * Fires when the map show.
29967          * @param {Roo.bootstrap.LocationPicker} this
29968          */
29969         show : true,
29970         /**
29971          * @event hide
29972          * Fires when the map hide.
29973          * @param {Roo.bootstrap.LocationPicker} this
29974          */
29975         hide : true,
29976         /**
29977          * @event mapClick
29978          * Fires when click the map.
29979          * @param {Roo.bootstrap.LocationPicker} this
29980          * @param {Map event} e
29981          */
29982         mapClick : true,
29983         /**
29984          * @event mapRightClick
29985          * Fires when right click the map.
29986          * @param {Roo.bootstrap.LocationPicker} this
29987          * @param {Map event} e
29988          */
29989         mapRightClick : true,
29990         /**
29991          * @event markerClick
29992          * Fires when click the marker.
29993          * @param {Roo.bootstrap.LocationPicker} this
29994          * @param {Map event} e
29995          */
29996         markerClick : true,
29997         /**
29998          * @event markerRightClick
29999          * Fires when right click the marker.
30000          * @param {Roo.bootstrap.LocationPicker} this
30001          * @param {Map event} e
30002          */
30003         markerRightClick : true,
30004         /**
30005          * @event OverlayViewDraw
30006          * Fires when OverlayView Draw
30007          * @param {Roo.bootstrap.LocationPicker} this
30008          */
30009         OverlayViewDraw : true,
30010         /**
30011          * @event OverlayViewOnAdd
30012          * Fires when OverlayView Draw
30013          * @param {Roo.bootstrap.LocationPicker} this
30014          */
30015         OverlayViewOnAdd : true,
30016         /**
30017          * @event OverlayViewOnRemove
30018          * Fires when OverlayView Draw
30019          * @param {Roo.bootstrap.LocationPicker} this
30020          */
30021         OverlayViewOnRemove : true,
30022         /**
30023          * @event OverlayViewShow
30024          * Fires when OverlayView Draw
30025          * @param {Roo.bootstrap.LocationPicker} this
30026          * @param {Pixel} cpx
30027          */
30028         OverlayViewShow : true,
30029         /**
30030          * @event OverlayViewHide
30031          * Fires when OverlayView Draw
30032          * @param {Roo.bootstrap.LocationPicker} this
30033          */
30034         OverlayViewHide : true,
30035         /**
30036          * @event loadexception
30037          * Fires when load google lib failed.
30038          * @param {Roo.bootstrap.LocationPicker} this
30039          */
30040         loadexception : true
30041     });
30042         
30043 };
30044
30045 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30046     
30047     gMapContext: false,
30048     
30049     latitude: 0,
30050     longitude: 0,
30051     zoom: 15,
30052     mapTypeId: false,
30053     mapTypeControl: false,
30054     disableDoubleClickZoom: false,
30055     scrollwheel: true,
30056     streetViewControl: false,
30057     radius: 0,
30058     locationName: '',
30059     draggable: true,
30060     enableAutocomplete: false,
30061     enableReverseGeocode: true,
30062     markerTitle: '',
30063     
30064     getAutoCreate: function()
30065     {
30066
30067         var cfg = {
30068             tag: 'div',
30069             cls: 'roo-location-picker'
30070         };
30071         
30072         return cfg
30073     },
30074     
30075     initEvents: function(ct, position)
30076     {       
30077         if(!this.el.getWidth() || this.isApplied()){
30078             return;
30079         }
30080         
30081         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30082         
30083         this.initial();
30084     },
30085     
30086     initial: function()
30087     {
30088         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30089             this.fireEvent('loadexception', this);
30090             return;
30091         }
30092         
30093         if(!this.mapTypeId){
30094             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30095         }
30096         
30097         this.gMapContext = this.GMapContext();
30098         
30099         this.initOverlayView();
30100         
30101         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30102         
30103         var _this = this;
30104                 
30105         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30106             _this.setPosition(_this.gMapContext.marker.position);
30107         });
30108         
30109         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30110             _this.fireEvent('mapClick', this, event);
30111             
30112         });
30113
30114         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30115             _this.fireEvent('mapRightClick', this, event);
30116             
30117         });
30118         
30119         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30120             _this.fireEvent('markerClick', this, event);
30121             
30122         });
30123
30124         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30125             _this.fireEvent('markerRightClick', this, event);
30126             
30127         });
30128         
30129         this.setPosition(this.gMapContext.location);
30130         
30131         this.fireEvent('initial', this, this.gMapContext.location);
30132     },
30133     
30134     initOverlayView: function()
30135     {
30136         var _this = this;
30137         
30138         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30139             
30140             draw: function()
30141             {
30142                 _this.fireEvent('OverlayViewDraw', _this);
30143             },
30144             
30145             onAdd: function()
30146             {
30147                 _this.fireEvent('OverlayViewOnAdd', _this);
30148             },
30149             
30150             onRemove: function()
30151             {
30152                 _this.fireEvent('OverlayViewOnRemove', _this);
30153             },
30154             
30155             show: function(cpx)
30156             {
30157                 _this.fireEvent('OverlayViewShow', _this, cpx);
30158             },
30159             
30160             hide: function()
30161             {
30162                 _this.fireEvent('OverlayViewHide', _this);
30163             }
30164             
30165         });
30166     },
30167     
30168     fromLatLngToContainerPixel: function(event)
30169     {
30170         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30171     },
30172     
30173     isApplied: function() 
30174     {
30175         return this.getGmapContext() == false ? false : true;
30176     },
30177     
30178     getGmapContext: function() 
30179     {
30180         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30181     },
30182     
30183     GMapContext: function() 
30184     {
30185         var position = new google.maps.LatLng(this.latitude, this.longitude);
30186         
30187         var _map = new google.maps.Map(this.el.dom, {
30188             center: position,
30189             zoom: this.zoom,
30190             mapTypeId: this.mapTypeId,
30191             mapTypeControl: this.mapTypeControl,
30192             disableDoubleClickZoom: this.disableDoubleClickZoom,
30193             scrollwheel: this.scrollwheel,
30194             streetViewControl: this.streetViewControl,
30195             locationName: this.locationName,
30196             draggable: this.draggable,
30197             enableAutocomplete: this.enableAutocomplete,
30198             enableReverseGeocode: this.enableReverseGeocode
30199         });
30200         
30201         var _marker = new google.maps.Marker({
30202             position: position,
30203             map: _map,
30204             title: this.markerTitle,
30205             draggable: this.draggable
30206         });
30207         
30208         return {
30209             map: _map,
30210             marker: _marker,
30211             circle: null,
30212             location: position,
30213             radius: this.radius,
30214             locationName: this.locationName,
30215             addressComponents: {
30216                 formatted_address: null,
30217                 addressLine1: null,
30218                 addressLine2: null,
30219                 streetName: null,
30220                 streetNumber: null,
30221                 city: null,
30222                 district: null,
30223                 state: null,
30224                 stateOrProvince: null
30225             },
30226             settings: this,
30227             domContainer: this.el.dom,
30228             geodecoder: new google.maps.Geocoder()
30229         };
30230     },
30231     
30232     drawCircle: function(center, radius, options) 
30233     {
30234         if (this.gMapContext.circle != null) {
30235             this.gMapContext.circle.setMap(null);
30236         }
30237         if (radius > 0) {
30238             radius *= 1;
30239             options = Roo.apply({}, options, {
30240                 strokeColor: "#0000FF",
30241                 strokeOpacity: .35,
30242                 strokeWeight: 2,
30243                 fillColor: "#0000FF",
30244                 fillOpacity: .2
30245             });
30246             
30247             options.map = this.gMapContext.map;
30248             options.radius = radius;
30249             options.center = center;
30250             this.gMapContext.circle = new google.maps.Circle(options);
30251             return this.gMapContext.circle;
30252         }
30253         
30254         return null;
30255     },
30256     
30257     setPosition: function(location) 
30258     {
30259         this.gMapContext.location = location;
30260         this.gMapContext.marker.setPosition(location);
30261         this.gMapContext.map.panTo(location);
30262         this.drawCircle(location, this.gMapContext.radius, {});
30263         
30264         var _this = this;
30265         
30266         if (this.gMapContext.settings.enableReverseGeocode) {
30267             this.gMapContext.geodecoder.geocode({
30268                 latLng: this.gMapContext.location
30269             }, function(results, status) {
30270                 
30271                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30272                     _this.gMapContext.locationName = results[0].formatted_address;
30273                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30274                     
30275                     _this.fireEvent('positionchanged', this, location);
30276                 }
30277             });
30278             
30279             return;
30280         }
30281         
30282         this.fireEvent('positionchanged', this, location);
30283     },
30284     
30285     resize: function()
30286     {
30287         google.maps.event.trigger(this.gMapContext.map, "resize");
30288         
30289         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30290         
30291         this.fireEvent('resize', this);
30292     },
30293     
30294     setPositionByLatLng: function(latitude, longitude)
30295     {
30296         this.setPosition(new google.maps.LatLng(latitude, longitude));
30297     },
30298     
30299     getCurrentPosition: function() 
30300     {
30301         return {
30302             latitude: this.gMapContext.location.lat(),
30303             longitude: this.gMapContext.location.lng()
30304         };
30305     },
30306     
30307     getAddressName: function() 
30308     {
30309         return this.gMapContext.locationName;
30310     },
30311     
30312     getAddressComponents: function() 
30313     {
30314         return this.gMapContext.addressComponents;
30315     },
30316     
30317     address_component_from_google_geocode: function(address_components) 
30318     {
30319         var result = {};
30320         
30321         for (var i = 0; i < address_components.length; i++) {
30322             var component = address_components[i];
30323             if (component.types.indexOf("postal_code") >= 0) {
30324                 result.postalCode = component.short_name;
30325             } else if (component.types.indexOf("street_number") >= 0) {
30326                 result.streetNumber = component.short_name;
30327             } else if (component.types.indexOf("route") >= 0) {
30328                 result.streetName = component.short_name;
30329             } else if (component.types.indexOf("neighborhood") >= 0) {
30330                 result.city = component.short_name;
30331             } else if (component.types.indexOf("locality") >= 0) {
30332                 result.city = component.short_name;
30333             } else if (component.types.indexOf("sublocality") >= 0) {
30334                 result.district = component.short_name;
30335             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30336                 result.stateOrProvince = component.short_name;
30337             } else if (component.types.indexOf("country") >= 0) {
30338                 result.country = component.short_name;
30339             }
30340         }
30341         
30342         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30343         result.addressLine2 = "";
30344         return result;
30345     },
30346     
30347     setZoomLevel: function(zoom)
30348     {
30349         this.gMapContext.map.setZoom(zoom);
30350     },
30351     
30352     show: function()
30353     {
30354         if(!this.el){
30355             return;
30356         }
30357         
30358         this.el.show();
30359         
30360         this.resize();
30361         
30362         this.fireEvent('show', this);
30363     },
30364     
30365     hide: function()
30366     {
30367         if(!this.el){
30368             return;
30369         }
30370         
30371         this.el.hide();
30372         
30373         this.fireEvent('hide', this);
30374     }
30375     
30376 });
30377
30378 Roo.apply(Roo.bootstrap.LocationPicker, {
30379     
30380     OverlayView : function(map, options)
30381     {
30382         options = options || {};
30383         
30384         this.setMap(map);
30385     }
30386     
30387     
30388 });/**
30389  * @class Roo.bootstrap.Alert
30390  * @extends Roo.bootstrap.Component
30391  * Bootstrap Alert class - shows an alert area box
30392  * eg
30393  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30394   Enter a valid email address
30395 </div>
30396  * @licence LGPL
30397  * @cfg {String} title The title of alert
30398  * @cfg {String} html The content of alert
30399  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30400  * @cfg {String} fa font-awesomeicon
30401  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30402  * @cfg {Boolean} close true to show a x closer
30403  * 
30404  * 
30405  * @constructor
30406  * Create a new alert
30407  * @param {Object} config The config object
30408  */
30409
30410
30411 Roo.bootstrap.Alert = function(config){
30412     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30413     
30414 };
30415
30416 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30417     
30418     title: '',
30419     html: '',
30420     weight: false,
30421     fa: false,
30422     faicon: false, // BC
30423     close : false,
30424     
30425     
30426     getAutoCreate : function()
30427     {
30428         
30429         var cfg = {
30430             tag : 'div',
30431             cls : 'alert',
30432             cn : [
30433                 {
30434                     tag: 'button',
30435                     type :  "button",
30436                     cls: "close",
30437                     html : '×',
30438                     style : this.close ? '' : 'display:none'
30439                 },
30440                 {
30441                     tag : 'i',
30442                     cls : 'roo-alert-icon'
30443                     
30444                 },
30445                 {
30446                     tag : 'b',
30447                     cls : 'roo-alert-title',
30448                     html : this.title
30449                 },
30450                 {
30451                     tag : 'span',
30452                     cls : 'roo-alert-text',
30453                     html : this.html
30454                 }
30455             ]
30456         };
30457         
30458         if(this.faicon){
30459             cfg.cn[0].cls += ' fa ' + this.faicon;
30460         }
30461         if(this.fa){
30462             cfg.cn[0].cls += ' fa ' + this.fa;
30463         }
30464         
30465         if(this.weight){
30466             cfg.cls += ' alert-' + this.weight;
30467         }
30468         
30469         return cfg;
30470     },
30471     
30472     initEvents: function() 
30473     {
30474         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30475         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30476         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30477         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30478         if (this.seconds > 0) {
30479             this.hide.defer(this.seconds, this);
30480         }
30481     },
30482     /**
30483      * Set the Title Message HTML
30484      * @param {String} html
30485      */
30486     setTitle : function(str)
30487     {
30488         this.titleEl.dom.innerHTML = str;
30489     },
30490      
30491      /**
30492      * Set the Body Message HTML
30493      * @param {String} html
30494      */
30495     setHtml : function(str)
30496     {
30497         this.htmlEl.dom.innerHTML = str;
30498     },
30499     /**
30500      * Set the Weight of the alert
30501      * @param {String} (success|info|warning|danger) weight
30502      */
30503     
30504     setWeight : function(weight)
30505     {
30506         if(this.weight){
30507             this.el.removeClass('alert-' + this.weight);
30508         }
30509         
30510         this.weight = weight;
30511         
30512         this.el.addClass('alert-' + this.weight);
30513     },
30514       /**
30515      * Set the Icon of the alert
30516      * @param {String} see fontawsome names (name without the 'fa-' bit)
30517      */
30518     setIcon : function(icon)
30519     {
30520         if(this.faicon){
30521             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30522         }
30523         
30524         this.faicon = icon;
30525         
30526         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30527     },
30528     /**
30529      * Hide the Alert
30530      */
30531     hide: function() 
30532     {
30533         this.el.hide();   
30534     },
30535     /**
30536      * Show the Alert
30537      */
30538     show: function() 
30539     {  
30540         this.el.show();   
30541     }
30542     
30543 });
30544
30545  
30546 /*
30547 * Licence: LGPL
30548 */
30549
30550 /**
30551  * @class Roo.bootstrap.UploadCropbox
30552  * @extends Roo.bootstrap.Component
30553  * Bootstrap UploadCropbox class
30554  * @cfg {String} emptyText show when image has been loaded
30555  * @cfg {String} rotateNotify show when image too small to rotate
30556  * @cfg {Number} errorTimeout default 3000
30557  * @cfg {Number} minWidth default 300
30558  * @cfg {Number} minHeight default 300
30559  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30560  * @cfg {Boolean} isDocument (true|false) default false
30561  * @cfg {String} url action url
30562  * @cfg {String} paramName default 'imageUpload'
30563  * @cfg {String} method default POST
30564  * @cfg {Boolean} loadMask (true|false) default true
30565  * @cfg {Boolean} loadingText default 'Loading...'
30566  * 
30567  * @constructor
30568  * Create a new UploadCropbox
30569  * @param {Object} config The config object
30570  */
30571
30572 Roo.bootstrap.UploadCropbox = function(config){
30573     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30574     
30575     this.addEvents({
30576         /**
30577          * @event beforeselectfile
30578          * Fire before select file
30579          * @param {Roo.bootstrap.UploadCropbox} this
30580          */
30581         "beforeselectfile" : true,
30582         /**
30583          * @event initial
30584          * Fire after initEvent
30585          * @param {Roo.bootstrap.UploadCropbox} this
30586          */
30587         "initial" : true,
30588         /**
30589          * @event crop
30590          * Fire after initEvent
30591          * @param {Roo.bootstrap.UploadCropbox} this
30592          * @param {String} data
30593          */
30594         "crop" : true,
30595         /**
30596          * @event prepare
30597          * Fire when preparing the file data
30598          * @param {Roo.bootstrap.UploadCropbox} this
30599          * @param {Object} file
30600          */
30601         "prepare" : true,
30602         /**
30603          * @event exception
30604          * Fire when get exception
30605          * @param {Roo.bootstrap.UploadCropbox} this
30606          * @param {XMLHttpRequest} xhr
30607          */
30608         "exception" : true,
30609         /**
30610          * @event beforeloadcanvas
30611          * Fire before load the canvas
30612          * @param {Roo.bootstrap.UploadCropbox} this
30613          * @param {String} src
30614          */
30615         "beforeloadcanvas" : true,
30616         /**
30617          * @event trash
30618          * Fire when trash image
30619          * @param {Roo.bootstrap.UploadCropbox} this
30620          */
30621         "trash" : true,
30622         /**
30623          * @event download
30624          * Fire when download the image
30625          * @param {Roo.bootstrap.UploadCropbox} this
30626          */
30627         "download" : true,
30628         /**
30629          * @event footerbuttonclick
30630          * Fire when footerbuttonclick
30631          * @param {Roo.bootstrap.UploadCropbox} this
30632          * @param {String} type
30633          */
30634         "footerbuttonclick" : true,
30635         /**
30636          * @event resize
30637          * Fire when resize
30638          * @param {Roo.bootstrap.UploadCropbox} this
30639          */
30640         "resize" : true,
30641         /**
30642          * @event rotate
30643          * Fire when rotate the image
30644          * @param {Roo.bootstrap.UploadCropbox} this
30645          * @param {String} pos
30646          */
30647         "rotate" : true,
30648         /**
30649          * @event inspect
30650          * Fire when inspect the file
30651          * @param {Roo.bootstrap.UploadCropbox} this
30652          * @param {Object} file
30653          */
30654         "inspect" : true,
30655         /**
30656          * @event upload
30657          * Fire when xhr upload the file
30658          * @param {Roo.bootstrap.UploadCropbox} this
30659          * @param {Object} data
30660          */
30661         "upload" : true,
30662         /**
30663          * @event arrange
30664          * Fire when arrange the file data
30665          * @param {Roo.bootstrap.UploadCropbox} this
30666          * @param {Object} formData
30667          */
30668         "arrange" : true
30669     });
30670     
30671     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30672 };
30673
30674 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30675     
30676     emptyText : 'Click to upload image',
30677     rotateNotify : 'Image is too small to rotate',
30678     errorTimeout : 3000,
30679     scale : 0,
30680     baseScale : 1,
30681     rotate : 0,
30682     dragable : false,
30683     pinching : false,
30684     mouseX : 0,
30685     mouseY : 0,
30686     cropData : false,
30687     minWidth : 300,
30688     minHeight : 300,
30689     file : false,
30690     exif : {},
30691     baseRotate : 1,
30692     cropType : 'image/jpeg',
30693     buttons : false,
30694     canvasLoaded : false,
30695     isDocument : false,
30696     method : 'POST',
30697     paramName : 'imageUpload',
30698     loadMask : true,
30699     loadingText : 'Loading...',
30700     maskEl : false,
30701     
30702     getAutoCreate : function()
30703     {
30704         var cfg = {
30705             tag : 'div',
30706             cls : 'roo-upload-cropbox',
30707             cn : [
30708                 {
30709                     tag : 'input',
30710                     cls : 'roo-upload-cropbox-selector',
30711                     type : 'file'
30712                 },
30713                 {
30714                     tag : 'div',
30715                     cls : 'roo-upload-cropbox-body',
30716                     style : 'cursor:pointer',
30717                     cn : [
30718                         {
30719                             tag : 'div',
30720                             cls : 'roo-upload-cropbox-preview'
30721                         },
30722                         {
30723                             tag : 'div',
30724                             cls : 'roo-upload-cropbox-thumb'
30725                         },
30726                         {
30727                             tag : 'div',
30728                             cls : 'roo-upload-cropbox-empty-notify',
30729                             html : this.emptyText
30730                         },
30731                         {
30732                             tag : 'div',
30733                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30734                             html : this.rotateNotify
30735                         }
30736                     ]
30737                 },
30738                 {
30739                     tag : 'div',
30740                     cls : 'roo-upload-cropbox-footer',
30741                     cn : {
30742                         tag : 'div',
30743                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30744                         cn : []
30745                     }
30746                 }
30747             ]
30748         };
30749         
30750         return cfg;
30751     },
30752     
30753     onRender : function(ct, position)
30754     {
30755         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30756         
30757         if (this.buttons.length) {
30758             
30759             Roo.each(this.buttons, function(bb) {
30760                 
30761                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30762                 
30763                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30764                 
30765             }, this);
30766         }
30767         
30768         if(this.loadMask){
30769             this.maskEl = this.el;
30770         }
30771     },
30772     
30773     initEvents : function()
30774     {
30775         this.urlAPI = (window.createObjectURL && window) || 
30776                                 (window.URL && URL.revokeObjectURL && URL) || 
30777                                 (window.webkitURL && webkitURL);
30778                         
30779         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30780         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30781         
30782         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30783         this.selectorEl.hide();
30784         
30785         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30786         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30787         
30788         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30789         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30790         this.thumbEl.hide();
30791         
30792         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30793         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30794         
30795         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30796         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30797         this.errorEl.hide();
30798         
30799         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30800         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30801         this.footerEl.hide();
30802         
30803         this.setThumbBoxSize();
30804         
30805         this.bind();
30806         
30807         this.resize();
30808         
30809         this.fireEvent('initial', this);
30810     },
30811
30812     bind : function()
30813     {
30814         var _this = this;
30815         
30816         window.addEventListener("resize", function() { _this.resize(); } );
30817         
30818         this.bodyEl.on('click', this.beforeSelectFile, this);
30819         
30820         if(Roo.isTouch){
30821             this.bodyEl.on('touchstart', this.onTouchStart, this);
30822             this.bodyEl.on('touchmove', this.onTouchMove, this);
30823             this.bodyEl.on('touchend', this.onTouchEnd, this);
30824         }
30825         
30826         if(!Roo.isTouch){
30827             this.bodyEl.on('mousedown', this.onMouseDown, this);
30828             this.bodyEl.on('mousemove', this.onMouseMove, this);
30829             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30830             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30831             Roo.get(document).on('mouseup', this.onMouseUp, this);
30832         }
30833         
30834         this.selectorEl.on('change', this.onFileSelected, this);
30835     },
30836     
30837     reset : function()
30838     {    
30839         this.scale = 0;
30840         this.baseScale = 1;
30841         this.rotate = 0;
30842         this.baseRotate = 1;
30843         this.dragable = false;
30844         this.pinching = false;
30845         this.mouseX = 0;
30846         this.mouseY = 0;
30847         this.cropData = false;
30848         this.notifyEl.dom.innerHTML = this.emptyText;
30849         
30850         this.selectorEl.dom.value = '';
30851         
30852     },
30853     
30854     resize : function()
30855     {
30856         if(this.fireEvent('resize', this) != false){
30857             this.setThumbBoxPosition();
30858             this.setCanvasPosition();
30859         }
30860     },
30861     
30862     onFooterButtonClick : function(e, el, o, type)
30863     {
30864         switch (type) {
30865             case 'rotate-left' :
30866                 this.onRotateLeft(e);
30867                 break;
30868             case 'rotate-right' :
30869                 this.onRotateRight(e);
30870                 break;
30871             case 'picture' :
30872                 this.beforeSelectFile(e);
30873                 break;
30874             case 'trash' :
30875                 this.trash(e);
30876                 break;
30877             case 'crop' :
30878                 this.crop(e);
30879                 break;
30880             case 'download' :
30881                 this.download(e);
30882                 break;
30883             default :
30884                 break;
30885         }
30886         
30887         this.fireEvent('footerbuttonclick', this, type);
30888     },
30889     
30890     beforeSelectFile : function(e)
30891     {
30892         e.preventDefault();
30893         
30894         if(this.fireEvent('beforeselectfile', this) != false){
30895             this.selectorEl.dom.click();
30896         }
30897     },
30898     
30899     onFileSelected : function(e)
30900     {
30901         e.preventDefault();
30902         
30903         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30904             return;
30905         }
30906         
30907         var file = this.selectorEl.dom.files[0];
30908         
30909         if(this.fireEvent('inspect', this, file) != false){
30910             this.prepare(file);
30911         }
30912         
30913     },
30914     
30915     trash : function(e)
30916     {
30917         this.fireEvent('trash', this);
30918     },
30919     
30920     download : function(e)
30921     {
30922         this.fireEvent('download', this);
30923     },
30924     
30925     loadCanvas : function(src)
30926     {   
30927         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30928             
30929             this.reset();
30930             
30931             this.imageEl = document.createElement('img');
30932             
30933             var _this = this;
30934             
30935             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30936             
30937             this.imageEl.src = src;
30938         }
30939     },
30940     
30941     onLoadCanvas : function()
30942     {   
30943         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30944         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30945         
30946         this.bodyEl.un('click', this.beforeSelectFile, this);
30947         
30948         this.notifyEl.hide();
30949         this.thumbEl.show();
30950         this.footerEl.show();
30951         
30952         this.baseRotateLevel();
30953         
30954         if(this.isDocument){
30955             this.setThumbBoxSize();
30956         }
30957         
30958         this.setThumbBoxPosition();
30959         
30960         this.baseScaleLevel();
30961         
30962         this.draw();
30963         
30964         this.resize();
30965         
30966         this.canvasLoaded = true;
30967         
30968         if(this.loadMask){
30969             this.maskEl.unmask();
30970         }
30971         
30972     },
30973     
30974     setCanvasPosition : function()
30975     {   
30976         if(!this.canvasEl){
30977             return;
30978         }
30979         
30980         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30981         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30982         
30983         this.previewEl.setLeft(pw);
30984         this.previewEl.setTop(ph);
30985         
30986     },
30987     
30988     onMouseDown : function(e)
30989     {   
30990         e.stopEvent();
30991         
30992         this.dragable = true;
30993         this.pinching = false;
30994         
30995         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30996             this.dragable = false;
30997             return;
30998         }
30999         
31000         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31001         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31002         
31003     },
31004     
31005     onMouseMove : function(e)
31006     {   
31007         e.stopEvent();
31008         
31009         if(!this.canvasLoaded){
31010             return;
31011         }
31012         
31013         if (!this.dragable){
31014             return;
31015         }
31016         
31017         var minX = Math.ceil(this.thumbEl.getLeft(true));
31018         var minY = Math.ceil(this.thumbEl.getTop(true));
31019         
31020         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31021         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31022         
31023         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31024         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31025         
31026         x = x - this.mouseX;
31027         y = y - this.mouseY;
31028         
31029         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31030         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31031         
31032         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31033         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31034         
31035         this.previewEl.setLeft(bgX);
31036         this.previewEl.setTop(bgY);
31037         
31038         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31039         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31040     },
31041     
31042     onMouseUp : function(e)
31043     {   
31044         e.stopEvent();
31045         
31046         this.dragable = false;
31047     },
31048     
31049     onMouseWheel : function(e)
31050     {   
31051         e.stopEvent();
31052         
31053         this.startScale = this.scale;
31054         
31055         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31056         
31057         if(!this.zoomable()){
31058             this.scale = this.startScale;
31059             return;
31060         }
31061         
31062         this.draw();
31063         
31064         return;
31065     },
31066     
31067     zoomable : function()
31068     {
31069         var minScale = this.thumbEl.getWidth() / this.minWidth;
31070         
31071         if(this.minWidth < this.minHeight){
31072             minScale = this.thumbEl.getHeight() / this.minHeight;
31073         }
31074         
31075         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31076         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31077         
31078         if(
31079                 this.isDocument &&
31080                 (this.rotate == 0 || this.rotate == 180) && 
31081                 (
31082                     width > this.imageEl.OriginWidth || 
31083                     height > this.imageEl.OriginHeight ||
31084                     (width < this.minWidth && height < this.minHeight)
31085                 )
31086         ){
31087             return false;
31088         }
31089         
31090         if(
31091                 this.isDocument &&
31092                 (this.rotate == 90 || this.rotate == 270) && 
31093                 (
31094                     width > this.imageEl.OriginWidth || 
31095                     height > this.imageEl.OriginHeight ||
31096                     (width < this.minHeight && height < this.minWidth)
31097                 )
31098         ){
31099             return false;
31100         }
31101         
31102         if(
31103                 !this.isDocument &&
31104                 (this.rotate == 0 || this.rotate == 180) && 
31105                 (
31106                     width < this.minWidth || 
31107                     width > this.imageEl.OriginWidth || 
31108                     height < this.minHeight || 
31109                     height > this.imageEl.OriginHeight
31110                 )
31111         ){
31112             return false;
31113         }
31114         
31115         if(
31116                 !this.isDocument &&
31117                 (this.rotate == 90 || this.rotate == 270) && 
31118                 (
31119                     width < this.minHeight || 
31120                     width > this.imageEl.OriginWidth || 
31121                     height < this.minWidth || 
31122                     height > this.imageEl.OriginHeight
31123                 )
31124         ){
31125             return false;
31126         }
31127         
31128         return true;
31129         
31130     },
31131     
31132     onRotateLeft : function(e)
31133     {   
31134         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31135             
31136             var minScale = this.thumbEl.getWidth() / this.minWidth;
31137             
31138             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31139             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31140             
31141             this.startScale = this.scale;
31142             
31143             while (this.getScaleLevel() < minScale){
31144             
31145                 this.scale = this.scale + 1;
31146                 
31147                 if(!this.zoomable()){
31148                     break;
31149                 }
31150                 
31151                 if(
31152                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31153                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31154                 ){
31155                     continue;
31156                 }
31157                 
31158                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31159
31160                 this.draw();
31161                 
31162                 return;
31163             }
31164             
31165             this.scale = this.startScale;
31166             
31167             this.onRotateFail();
31168             
31169             return false;
31170         }
31171         
31172         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31173
31174         if(this.isDocument){
31175             this.setThumbBoxSize();
31176             this.setThumbBoxPosition();
31177             this.setCanvasPosition();
31178         }
31179         
31180         this.draw();
31181         
31182         this.fireEvent('rotate', this, 'left');
31183         
31184     },
31185     
31186     onRotateRight : function(e)
31187     {
31188         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31189             
31190             var minScale = this.thumbEl.getWidth() / this.minWidth;
31191         
31192             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31193             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31194             
31195             this.startScale = this.scale;
31196             
31197             while (this.getScaleLevel() < minScale){
31198             
31199                 this.scale = this.scale + 1;
31200                 
31201                 if(!this.zoomable()){
31202                     break;
31203                 }
31204                 
31205                 if(
31206                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31207                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31208                 ){
31209                     continue;
31210                 }
31211                 
31212                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31213
31214                 this.draw();
31215                 
31216                 return;
31217             }
31218             
31219             this.scale = this.startScale;
31220             
31221             this.onRotateFail();
31222             
31223             return false;
31224         }
31225         
31226         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31227
31228         if(this.isDocument){
31229             this.setThumbBoxSize();
31230             this.setThumbBoxPosition();
31231             this.setCanvasPosition();
31232         }
31233         
31234         this.draw();
31235         
31236         this.fireEvent('rotate', this, 'right');
31237     },
31238     
31239     onRotateFail : function()
31240     {
31241         this.errorEl.show(true);
31242         
31243         var _this = this;
31244         
31245         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31246     },
31247     
31248     draw : function()
31249     {
31250         this.previewEl.dom.innerHTML = '';
31251         
31252         var canvasEl = document.createElement("canvas");
31253         
31254         var contextEl = canvasEl.getContext("2d");
31255         
31256         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31257         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31258         var center = this.imageEl.OriginWidth / 2;
31259         
31260         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31261             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31262             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31263             center = this.imageEl.OriginHeight / 2;
31264         }
31265         
31266         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31267         
31268         contextEl.translate(center, center);
31269         contextEl.rotate(this.rotate * Math.PI / 180);
31270
31271         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31272         
31273         this.canvasEl = document.createElement("canvas");
31274         
31275         this.contextEl = this.canvasEl.getContext("2d");
31276         
31277         switch (this.rotate) {
31278             case 0 :
31279                 
31280                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31281                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31282                 
31283                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31284                 
31285                 break;
31286             case 90 : 
31287                 
31288                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31289                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31290                 
31291                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31292                     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);
31293                     break;
31294                 }
31295                 
31296                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31297                 
31298                 break;
31299             case 180 :
31300                 
31301                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31302                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31303                 
31304                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31305                     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);
31306                     break;
31307                 }
31308                 
31309                 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);
31310                 
31311                 break;
31312             case 270 :
31313                 
31314                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31315                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31316         
31317                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31318                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31319                     break;
31320                 }
31321                 
31322                 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);
31323                 
31324                 break;
31325             default : 
31326                 break;
31327         }
31328         
31329         this.previewEl.appendChild(this.canvasEl);
31330         
31331         this.setCanvasPosition();
31332     },
31333     
31334     crop : function()
31335     {
31336         if(!this.canvasLoaded){
31337             return;
31338         }
31339         
31340         var imageCanvas = document.createElement("canvas");
31341         
31342         var imageContext = imageCanvas.getContext("2d");
31343         
31344         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31345         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31346         
31347         var center = imageCanvas.width / 2;
31348         
31349         imageContext.translate(center, center);
31350         
31351         imageContext.rotate(this.rotate * Math.PI / 180);
31352         
31353         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31354         
31355         var canvas = document.createElement("canvas");
31356         
31357         var context = canvas.getContext("2d");
31358                 
31359         canvas.width = this.minWidth;
31360         canvas.height = this.minHeight;
31361
31362         switch (this.rotate) {
31363             case 0 :
31364                 
31365                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31366                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31367                 
31368                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31369                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31370                 
31371                 var targetWidth = this.minWidth - 2 * x;
31372                 var targetHeight = this.minHeight - 2 * y;
31373                 
31374                 var scale = 1;
31375                 
31376                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31377                     scale = targetWidth / width;
31378                 }
31379                 
31380                 if(x > 0 && y == 0){
31381                     scale = targetHeight / height;
31382                 }
31383                 
31384                 if(x > 0 && y > 0){
31385                     scale = targetWidth / width;
31386                     
31387                     if(width < height){
31388                         scale = targetHeight / height;
31389                     }
31390                 }
31391                 
31392                 context.scale(scale, scale);
31393                 
31394                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31395                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31396
31397                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31398                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31399
31400                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31401                 
31402                 break;
31403             case 90 : 
31404                 
31405                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31406                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31407                 
31408                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31409                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31410                 
31411                 var targetWidth = this.minWidth - 2 * x;
31412                 var targetHeight = this.minHeight - 2 * y;
31413                 
31414                 var scale = 1;
31415                 
31416                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31417                     scale = targetWidth / width;
31418                 }
31419                 
31420                 if(x > 0 && y == 0){
31421                     scale = targetHeight / height;
31422                 }
31423                 
31424                 if(x > 0 && y > 0){
31425                     scale = targetWidth / width;
31426                     
31427                     if(width < height){
31428                         scale = targetHeight / height;
31429                     }
31430                 }
31431                 
31432                 context.scale(scale, scale);
31433                 
31434                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31435                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31436
31437                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31438                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31439                 
31440                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31441                 
31442                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31443                 
31444                 break;
31445             case 180 :
31446                 
31447                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31448                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31449                 
31450                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31451                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31452                 
31453                 var targetWidth = this.minWidth - 2 * x;
31454                 var targetHeight = this.minHeight - 2 * y;
31455                 
31456                 var scale = 1;
31457                 
31458                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31459                     scale = targetWidth / width;
31460                 }
31461                 
31462                 if(x > 0 && y == 0){
31463                     scale = targetHeight / height;
31464                 }
31465                 
31466                 if(x > 0 && y > 0){
31467                     scale = targetWidth / width;
31468                     
31469                     if(width < height){
31470                         scale = targetHeight / height;
31471                     }
31472                 }
31473                 
31474                 context.scale(scale, scale);
31475                 
31476                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31477                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31478
31479                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31480                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31481
31482                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31483                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31484                 
31485                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31486                 
31487                 break;
31488             case 270 :
31489                 
31490                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31491                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31492                 
31493                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31494                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31495                 
31496                 var targetWidth = this.minWidth - 2 * x;
31497                 var targetHeight = this.minHeight - 2 * y;
31498                 
31499                 var scale = 1;
31500                 
31501                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31502                     scale = targetWidth / width;
31503                 }
31504                 
31505                 if(x > 0 && y == 0){
31506                     scale = targetHeight / height;
31507                 }
31508                 
31509                 if(x > 0 && y > 0){
31510                     scale = targetWidth / width;
31511                     
31512                     if(width < height){
31513                         scale = targetHeight / height;
31514                     }
31515                 }
31516                 
31517                 context.scale(scale, scale);
31518                 
31519                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31520                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31521
31522                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31523                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31524                 
31525                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31526                 
31527                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31528                 
31529                 break;
31530             default : 
31531                 break;
31532         }
31533         
31534         this.cropData = canvas.toDataURL(this.cropType);
31535         
31536         if(this.fireEvent('crop', this, this.cropData) !== false){
31537             this.process(this.file, this.cropData);
31538         }
31539         
31540         return;
31541         
31542     },
31543     
31544     setThumbBoxSize : function()
31545     {
31546         var width, height;
31547         
31548         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31549             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31550             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31551             
31552             this.minWidth = width;
31553             this.minHeight = height;
31554             
31555             if(this.rotate == 90 || this.rotate == 270){
31556                 this.minWidth = height;
31557                 this.minHeight = width;
31558             }
31559         }
31560         
31561         height = 300;
31562         width = Math.ceil(this.minWidth * height / this.minHeight);
31563         
31564         if(this.minWidth > this.minHeight){
31565             width = 300;
31566             height = Math.ceil(this.minHeight * width / this.minWidth);
31567         }
31568         
31569         this.thumbEl.setStyle({
31570             width : width + 'px',
31571             height : height + 'px'
31572         });
31573
31574         return;
31575             
31576     },
31577     
31578     setThumbBoxPosition : function()
31579     {
31580         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31581         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31582         
31583         this.thumbEl.setLeft(x);
31584         this.thumbEl.setTop(y);
31585         
31586     },
31587     
31588     baseRotateLevel : function()
31589     {
31590         this.baseRotate = 1;
31591         
31592         if(
31593                 typeof(this.exif) != 'undefined' &&
31594                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31595                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31596         ){
31597             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31598         }
31599         
31600         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31601         
31602     },
31603     
31604     baseScaleLevel : function()
31605     {
31606         var width, height;
31607         
31608         if(this.isDocument){
31609             
31610             if(this.baseRotate == 6 || this.baseRotate == 8){
31611             
31612                 height = this.thumbEl.getHeight();
31613                 this.baseScale = height / this.imageEl.OriginWidth;
31614
31615                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31616                     width = this.thumbEl.getWidth();
31617                     this.baseScale = width / this.imageEl.OriginHeight;
31618                 }
31619
31620                 return;
31621             }
31622
31623             height = this.thumbEl.getHeight();
31624             this.baseScale = height / this.imageEl.OriginHeight;
31625
31626             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31627                 width = this.thumbEl.getWidth();
31628                 this.baseScale = width / this.imageEl.OriginWidth;
31629             }
31630
31631             return;
31632         }
31633         
31634         if(this.baseRotate == 6 || this.baseRotate == 8){
31635             
31636             width = this.thumbEl.getHeight();
31637             this.baseScale = width / this.imageEl.OriginHeight;
31638             
31639             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31640                 height = this.thumbEl.getWidth();
31641                 this.baseScale = height / this.imageEl.OriginHeight;
31642             }
31643             
31644             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31645                 height = this.thumbEl.getWidth();
31646                 this.baseScale = height / this.imageEl.OriginHeight;
31647                 
31648                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31649                     width = this.thumbEl.getHeight();
31650                     this.baseScale = width / this.imageEl.OriginWidth;
31651                 }
31652             }
31653             
31654             return;
31655         }
31656         
31657         width = this.thumbEl.getWidth();
31658         this.baseScale = width / this.imageEl.OriginWidth;
31659         
31660         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31661             height = this.thumbEl.getHeight();
31662             this.baseScale = height / this.imageEl.OriginHeight;
31663         }
31664         
31665         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31666             
31667             height = this.thumbEl.getHeight();
31668             this.baseScale = height / this.imageEl.OriginHeight;
31669             
31670             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31671                 width = this.thumbEl.getWidth();
31672                 this.baseScale = width / this.imageEl.OriginWidth;
31673             }
31674             
31675         }
31676         
31677         return;
31678     },
31679     
31680     getScaleLevel : function()
31681     {
31682         return this.baseScale * Math.pow(1.1, this.scale);
31683     },
31684     
31685     onTouchStart : function(e)
31686     {
31687         if(!this.canvasLoaded){
31688             this.beforeSelectFile(e);
31689             return;
31690         }
31691         
31692         var touches = e.browserEvent.touches;
31693         
31694         if(!touches){
31695             return;
31696         }
31697         
31698         if(touches.length == 1){
31699             this.onMouseDown(e);
31700             return;
31701         }
31702         
31703         if(touches.length != 2){
31704             return;
31705         }
31706         
31707         var coords = [];
31708         
31709         for(var i = 0, finger; finger = touches[i]; i++){
31710             coords.push(finger.pageX, finger.pageY);
31711         }
31712         
31713         var x = Math.pow(coords[0] - coords[2], 2);
31714         var y = Math.pow(coords[1] - coords[3], 2);
31715         
31716         this.startDistance = Math.sqrt(x + y);
31717         
31718         this.startScale = this.scale;
31719         
31720         this.pinching = true;
31721         this.dragable = false;
31722         
31723     },
31724     
31725     onTouchMove : function(e)
31726     {
31727         if(!this.pinching && !this.dragable){
31728             return;
31729         }
31730         
31731         var touches = e.browserEvent.touches;
31732         
31733         if(!touches){
31734             return;
31735         }
31736         
31737         if(this.dragable){
31738             this.onMouseMove(e);
31739             return;
31740         }
31741         
31742         var coords = [];
31743         
31744         for(var i = 0, finger; finger = touches[i]; i++){
31745             coords.push(finger.pageX, finger.pageY);
31746         }
31747         
31748         var x = Math.pow(coords[0] - coords[2], 2);
31749         var y = Math.pow(coords[1] - coords[3], 2);
31750         
31751         this.endDistance = Math.sqrt(x + y);
31752         
31753         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31754         
31755         if(!this.zoomable()){
31756             this.scale = this.startScale;
31757             return;
31758         }
31759         
31760         this.draw();
31761         
31762     },
31763     
31764     onTouchEnd : function(e)
31765     {
31766         this.pinching = false;
31767         this.dragable = false;
31768         
31769     },
31770     
31771     process : function(file, crop)
31772     {
31773         if(this.loadMask){
31774             this.maskEl.mask(this.loadingText);
31775         }
31776         
31777         this.xhr = new XMLHttpRequest();
31778         
31779         file.xhr = this.xhr;
31780
31781         this.xhr.open(this.method, this.url, true);
31782         
31783         var headers = {
31784             "Accept": "application/json",
31785             "Cache-Control": "no-cache",
31786             "X-Requested-With": "XMLHttpRequest"
31787         };
31788         
31789         for (var headerName in headers) {
31790             var headerValue = headers[headerName];
31791             if (headerValue) {
31792                 this.xhr.setRequestHeader(headerName, headerValue);
31793             }
31794         }
31795         
31796         var _this = this;
31797         
31798         this.xhr.onload = function()
31799         {
31800             _this.xhrOnLoad(_this.xhr);
31801         }
31802         
31803         this.xhr.onerror = function()
31804         {
31805             _this.xhrOnError(_this.xhr);
31806         }
31807         
31808         var formData = new FormData();
31809
31810         formData.append('returnHTML', 'NO');
31811         
31812         if(crop){
31813             formData.append('crop', crop);
31814         }
31815         
31816         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31817             formData.append(this.paramName, file, file.name);
31818         }
31819         
31820         if(typeof(file.filename) != 'undefined'){
31821             formData.append('filename', file.filename);
31822         }
31823         
31824         if(typeof(file.mimetype) != 'undefined'){
31825             formData.append('mimetype', file.mimetype);
31826         }
31827         
31828         if(this.fireEvent('arrange', this, formData) != false){
31829             this.xhr.send(formData);
31830         };
31831     },
31832     
31833     xhrOnLoad : function(xhr)
31834     {
31835         if(this.loadMask){
31836             this.maskEl.unmask();
31837         }
31838         
31839         if (xhr.readyState !== 4) {
31840             this.fireEvent('exception', this, xhr);
31841             return;
31842         }
31843
31844         var response = Roo.decode(xhr.responseText);
31845         
31846         if(!response.success){
31847             this.fireEvent('exception', this, xhr);
31848             return;
31849         }
31850         
31851         var response = Roo.decode(xhr.responseText);
31852         
31853         this.fireEvent('upload', this, response);
31854         
31855     },
31856     
31857     xhrOnError : function()
31858     {
31859         if(this.loadMask){
31860             this.maskEl.unmask();
31861         }
31862         
31863         Roo.log('xhr on error');
31864         
31865         var response = Roo.decode(xhr.responseText);
31866           
31867         Roo.log(response);
31868         
31869     },
31870     
31871     prepare : function(file)
31872     {   
31873         if(this.loadMask){
31874             this.maskEl.mask(this.loadingText);
31875         }
31876         
31877         this.file = false;
31878         this.exif = {};
31879         
31880         if(typeof(file) === 'string'){
31881             this.loadCanvas(file);
31882             return;
31883         }
31884         
31885         if(!file || !this.urlAPI){
31886             return;
31887         }
31888         
31889         this.file = file;
31890         this.cropType = file.type;
31891         
31892         var _this = this;
31893         
31894         if(this.fireEvent('prepare', this, this.file) != false){
31895             
31896             var reader = new FileReader();
31897             
31898             reader.onload = function (e) {
31899                 if (e.target.error) {
31900                     Roo.log(e.target.error);
31901                     return;
31902                 }
31903                 
31904                 var buffer = e.target.result,
31905                     dataView = new DataView(buffer),
31906                     offset = 2,
31907                     maxOffset = dataView.byteLength - 4,
31908                     markerBytes,
31909                     markerLength;
31910                 
31911                 if (dataView.getUint16(0) === 0xffd8) {
31912                     while (offset < maxOffset) {
31913                         markerBytes = dataView.getUint16(offset);
31914                         
31915                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31916                             markerLength = dataView.getUint16(offset + 2) + 2;
31917                             if (offset + markerLength > dataView.byteLength) {
31918                                 Roo.log('Invalid meta data: Invalid segment size.');
31919                                 break;
31920                             }
31921                             
31922                             if(markerBytes == 0xffe1){
31923                                 _this.parseExifData(
31924                                     dataView,
31925                                     offset,
31926                                     markerLength
31927                                 );
31928                             }
31929                             
31930                             offset += markerLength;
31931                             
31932                             continue;
31933                         }
31934                         
31935                         break;
31936                     }
31937                     
31938                 }
31939                 
31940                 var url = _this.urlAPI.createObjectURL(_this.file);
31941                 
31942                 _this.loadCanvas(url);
31943                 
31944                 return;
31945             }
31946             
31947             reader.readAsArrayBuffer(this.file);
31948             
31949         }
31950         
31951     },
31952     
31953     parseExifData : function(dataView, offset, length)
31954     {
31955         var tiffOffset = offset + 10,
31956             littleEndian,
31957             dirOffset;
31958     
31959         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31960             // No Exif data, might be XMP data instead
31961             return;
31962         }
31963         
31964         // Check for the ASCII code for "Exif" (0x45786966):
31965         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31966             // No Exif data, might be XMP data instead
31967             return;
31968         }
31969         if (tiffOffset + 8 > dataView.byteLength) {
31970             Roo.log('Invalid Exif data: Invalid segment size.');
31971             return;
31972         }
31973         // Check for the two null bytes:
31974         if (dataView.getUint16(offset + 8) !== 0x0000) {
31975             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31976             return;
31977         }
31978         // Check the byte alignment:
31979         switch (dataView.getUint16(tiffOffset)) {
31980         case 0x4949:
31981             littleEndian = true;
31982             break;
31983         case 0x4D4D:
31984             littleEndian = false;
31985             break;
31986         default:
31987             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31988             return;
31989         }
31990         // Check for the TIFF tag marker (0x002A):
31991         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31992             Roo.log('Invalid Exif data: Missing TIFF marker.');
31993             return;
31994         }
31995         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31996         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31997         
31998         this.parseExifTags(
31999             dataView,
32000             tiffOffset,
32001             tiffOffset + dirOffset,
32002             littleEndian
32003         );
32004     },
32005     
32006     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32007     {
32008         var tagsNumber,
32009             dirEndOffset,
32010             i;
32011         if (dirOffset + 6 > dataView.byteLength) {
32012             Roo.log('Invalid Exif data: Invalid directory offset.');
32013             return;
32014         }
32015         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32016         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32017         if (dirEndOffset + 4 > dataView.byteLength) {
32018             Roo.log('Invalid Exif data: Invalid directory size.');
32019             return;
32020         }
32021         for (i = 0; i < tagsNumber; i += 1) {
32022             this.parseExifTag(
32023                 dataView,
32024                 tiffOffset,
32025                 dirOffset + 2 + 12 * i, // tag offset
32026                 littleEndian
32027             );
32028         }
32029         // Return the offset to the next directory:
32030         return dataView.getUint32(dirEndOffset, littleEndian);
32031     },
32032     
32033     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32034     {
32035         var tag = dataView.getUint16(offset, littleEndian);
32036         
32037         this.exif[tag] = this.getExifValue(
32038             dataView,
32039             tiffOffset,
32040             offset,
32041             dataView.getUint16(offset + 2, littleEndian), // tag type
32042             dataView.getUint32(offset + 4, littleEndian), // tag length
32043             littleEndian
32044         );
32045     },
32046     
32047     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32048     {
32049         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32050             tagSize,
32051             dataOffset,
32052             values,
32053             i,
32054             str,
32055             c;
32056     
32057         if (!tagType) {
32058             Roo.log('Invalid Exif data: Invalid tag type.');
32059             return;
32060         }
32061         
32062         tagSize = tagType.size * length;
32063         // Determine if the value is contained in the dataOffset bytes,
32064         // or if the value at the dataOffset is a pointer to the actual data:
32065         dataOffset = tagSize > 4 ?
32066                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32067         if (dataOffset + tagSize > dataView.byteLength) {
32068             Roo.log('Invalid Exif data: Invalid data offset.');
32069             return;
32070         }
32071         if (length === 1) {
32072             return tagType.getValue(dataView, dataOffset, littleEndian);
32073         }
32074         values = [];
32075         for (i = 0; i < length; i += 1) {
32076             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32077         }
32078         
32079         if (tagType.ascii) {
32080             str = '';
32081             // Concatenate the chars:
32082             for (i = 0; i < values.length; i += 1) {
32083                 c = values[i];
32084                 // Ignore the terminating NULL byte(s):
32085                 if (c === '\u0000') {
32086                     break;
32087                 }
32088                 str += c;
32089             }
32090             return str;
32091         }
32092         return values;
32093     }
32094     
32095 });
32096
32097 Roo.apply(Roo.bootstrap.UploadCropbox, {
32098     tags : {
32099         'Orientation': 0x0112
32100     },
32101     
32102     Orientation: {
32103             1: 0, //'top-left',
32104 //            2: 'top-right',
32105             3: 180, //'bottom-right',
32106 //            4: 'bottom-left',
32107 //            5: 'left-top',
32108             6: 90, //'right-top',
32109 //            7: 'right-bottom',
32110             8: 270 //'left-bottom'
32111     },
32112     
32113     exifTagTypes : {
32114         // byte, 8-bit unsigned int:
32115         1: {
32116             getValue: function (dataView, dataOffset) {
32117                 return dataView.getUint8(dataOffset);
32118             },
32119             size: 1
32120         },
32121         // ascii, 8-bit byte:
32122         2: {
32123             getValue: function (dataView, dataOffset) {
32124                 return String.fromCharCode(dataView.getUint8(dataOffset));
32125             },
32126             size: 1,
32127             ascii: true
32128         },
32129         // short, 16 bit int:
32130         3: {
32131             getValue: function (dataView, dataOffset, littleEndian) {
32132                 return dataView.getUint16(dataOffset, littleEndian);
32133             },
32134             size: 2
32135         },
32136         // long, 32 bit int:
32137         4: {
32138             getValue: function (dataView, dataOffset, littleEndian) {
32139                 return dataView.getUint32(dataOffset, littleEndian);
32140             },
32141             size: 4
32142         },
32143         // rational = two long values, first is numerator, second is denominator:
32144         5: {
32145             getValue: function (dataView, dataOffset, littleEndian) {
32146                 return dataView.getUint32(dataOffset, littleEndian) /
32147                     dataView.getUint32(dataOffset + 4, littleEndian);
32148             },
32149             size: 8
32150         },
32151         // slong, 32 bit signed int:
32152         9: {
32153             getValue: function (dataView, dataOffset, littleEndian) {
32154                 return dataView.getInt32(dataOffset, littleEndian);
32155             },
32156             size: 4
32157         },
32158         // srational, two slongs, first is numerator, second is denominator:
32159         10: {
32160             getValue: function (dataView, dataOffset, littleEndian) {
32161                 return dataView.getInt32(dataOffset, littleEndian) /
32162                     dataView.getInt32(dataOffset + 4, littleEndian);
32163             },
32164             size: 8
32165         }
32166     },
32167     
32168     footer : {
32169         STANDARD : [
32170             {
32171                 tag : 'div',
32172                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32173                 action : 'rotate-left',
32174                 cn : [
32175                     {
32176                         tag : 'button',
32177                         cls : 'btn btn-default',
32178                         html : '<i class="fa fa-undo"></i>'
32179                     }
32180                 ]
32181             },
32182             {
32183                 tag : 'div',
32184                 cls : 'btn-group roo-upload-cropbox-picture',
32185                 action : 'picture',
32186                 cn : [
32187                     {
32188                         tag : 'button',
32189                         cls : 'btn btn-default',
32190                         html : '<i class="fa fa-picture-o"></i>'
32191                     }
32192                 ]
32193             },
32194             {
32195                 tag : 'div',
32196                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32197                 action : 'rotate-right',
32198                 cn : [
32199                     {
32200                         tag : 'button',
32201                         cls : 'btn btn-default',
32202                         html : '<i class="fa fa-repeat"></i>'
32203                     }
32204                 ]
32205             }
32206         ],
32207         DOCUMENT : [
32208             {
32209                 tag : 'div',
32210                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32211                 action : 'rotate-left',
32212                 cn : [
32213                     {
32214                         tag : 'button',
32215                         cls : 'btn btn-default',
32216                         html : '<i class="fa fa-undo"></i>'
32217                     }
32218                 ]
32219             },
32220             {
32221                 tag : 'div',
32222                 cls : 'btn-group roo-upload-cropbox-download',
32223                 action : 'download',
32224                 cn : [
32225                     {
32226                         tag : 'button',
32227                         cls : 'btn btn-default',
32228                         html : '<i class="fa fa-download"></i>'
32229                     }
32230                 ]
32231             },
32232             {
32233                 tag : 'div',
32234                 cls : 'btn-group roo-upload-cropbox-crop',
32235                 action : 'crop',
32236                 cn : [
32237                     {
32238                         tag : 'button',
32239                         cls : 'btn btn-default',
32240                         html : '<i class="fa fa-crop"></i>'
32241                     }
32242                 ]
32243             },
32244             {
32245                 tag : 'div',
32246                 cls : 'btn-group roo-upload-cropbox-trash',
32247                 action : 'trash',
32248                 cn : [
32249                     {
32250                         tag : 'button',
32251                         cls : 'btn btn-default',
32252                         html : '<i class="fa fa-trash"></i>'
32253                     }
32254                 ]
32255             },
32256             {
32257                 tag : 'div',
32258                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32259                 action : 'rotate-right',
32260                 cn : [
32261                     {
32262                         tag : 'button',
32263                         cls : 'btn btn-default',
32264                         html : '<i class="fa fa-repeat"></i>'
32265                     }
32266                 ]
32267             }
32268         ],
32269         ROTATOR : [
32270             {
32271                 tag : 'div',
32272                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32273                 action : 'rotate-left',
32274                 cn : [
32275                     {
32276                         tag : 'button',
32277                         cls : 'btn btn-default',
32278                         html : '<i class="fa fa-undo"></i>'
32279                     }
32280                 ]
32281             },
32282             {
32283                 tag : 'div',
32284                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32285                 action : 'rotate-right',
32286                 cn : [
32287                     {
32288                         tag : 'button',
32289                         cls : 'btn btn-default',
32290                         html : '<i class="fa fa-repeat"></i>'
32291                     }
32292                 ]
32293             }
32294         ]
32295     }
32296 });
32297
32298 /*
32299 * Licence: LGPL
32300 */
32301
32302 /**
32303  * @class Roo.bootstrap.DocumentManager
32304  * @extends Roo.bootstrap.Component
32305  * Bootstrap DocumentManager class
32306  * @cfg {String} paramName default 'imageUpload'
32307  * @cfg {String} toolTipName default 'filename'
32308  * @cfg {String} method default POST
32309  * @cfg {String} url action url
32310  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32311  * @cfg {Boolean} multiple multiple upload default true
32312  * @cfg {Number} thumbSize default 300
32313  * @cfg {String} fieldLabel
32314  * @cfg {Number} labelWidth default 4
32315  * @cfg {String} labelAlign (left|top) default left
32316  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32317 * @cfg {Number} labellg set the width of label (1-12)
32318  * @cfg {Number} labelmd set the width of label (1-12)
32319  * @cfg {Number} labelsm set the width of label (1-12)
32320  * @cfg {Number} labelxs set the width of label (1-12)
32321  * 
32322  * @constructor
32323  * Create a new DocumentManager
32324  * @param {Object} config The config object
32325  */
32326
32327 Roo.bootstrap.DocumentManager = function(config){
32328     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32329     
32330     this.files = [];
32331     this.delegates = [];
32332     
32333     this.addEvents({
32334         /**
32335          * @event initial
32336          * Fire when initial the DocumentManager
32337          * @param {Roo.bootstrap.DocumentManager} this
32338          */
32339         "initial" : true,
32340         /**
32341          * @event inspect
32342          * inspect selected file
32343          * @param {Roo.bootstrap.DocumentManager} this
32344          * @param {File} file
32345          */
32346         "inspect" : true,
32347         /**
32348          * @event exception
32349          * Fire when xhr load exception
32350          * @param {Roo.bootstrap.DocumentManager} this
32351          * @param {XMLHttpRequest} xhr
32352          */
32353         "exception" : true,
32354         /**
32355          * @event afterupload
32356          * Fire when xhr load exception
32357          * @param {Roo.bootstrap.DocumentManager} this
32358          * @param {XMLHttpRequest} xhr
32359          */
32360         "afterupload" : true,
32361         /**
32362          * @event prepare
32363          * prepare the form data
32364          * @param {Roo.bootstrap.DocumentManager} this
32365          * @param {Object} formData
32366          */
32367         "prepare" : true,
32368         /**
32369          * @event remove
32370          * Fire when remove the file
32371          * @param {Roo.bootstrap.DocumentManager} this
32372          * @param {Object} file
32373          */
32374         "remove" : true,
32375         /**
32376          * @event refresh
32377          * Fire after refresh the file
32378          * @param {Roo.bootstrap.DocumentManager} this
32379          */
32380         "refresh" : true,
32381         /**
32382          * @event click
32383          * Fire after click the image
32384          * @param {Roo.bootstrap.DocumentManager} this
32385          * @param {Object} file
32386          */
32387         "click" : true,
32388         /**
32389          * @event edit
32390          * Fire when upload a image and editable set to true
32391          * @param {Roo.bootstrap.DocumentManager} this
32392          * @param {Object} file
32393          */
32394         "edit" : true,
32395         /**
32396          * @event beforeselectfile
32397          * Fire before select file
32398          * @param {Roo.bootstrap.DocumentManager} this
32399          */
32400         "beforeselectfile" : true,
32401         /**
32402          * @event process
32403          * Fire before process file
32404          * @param {Roo.bootstrap.DocumentManager} this
32405          * @param {Object} file
32406          */
32407         "process" : true,
32408         /**
32409          * @event previewrendered
32410          * Fire when preview rendered
32411          * @param {Roo.bootstrap.DocumentManager} this
32412          * @param {Object} file
32413          */
32414         "previewrendered" : true,
32415         /**
32416          */
32417         "previewResize" : true
32418         
32419     });
32420 };
32421
32422 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32423     
32424     boxes : 0,
32425     inputName : '',
32426     thumbSize : 300,
32427     multiple : true,
32428     files : false,
32429     method : 'POST',
32430     url : '',
32431     paramName : 'imageUpload',
32432     toolTipName : 'filename',
32433     fieldLabel : '',
32434     labelWidth : 4,
32435     labelAlign : 'left',
32436     editable : true,
32437     delegates : false,
32438     xhr : false, 
32439     
32440     labellg : 0,
32441     labelmd : 0,
32442     labelsm : 0,
32443     labelxs : 0,
32444     
32445     getAutoCreate : function()
32446     {   
32447         var managerWidget = {
32448             tag : 'div',
32449             cls : 'roo-document-manager',
32450             cn : [
32451                 {
32452                     tag : 'input',
32453                     cls : 'roo-document-manager-selector',
32454                     type : 'file'
32455                 },
32456                 {
32457                     tag : 'div',
32458                     cls : 'roo-document-manager-uploader',
32459                     cn : [
32460                         {
32461                             tag : 'div',
32462                             cls : 'roo-document-manager-upload-btn',
32463                             html : '<i class="fa fa-plus"></i>'
32464                         }
32465                     ]
32466                     
32467                 }
32468             ]
32469         };
32470         
32471         var content = [
32472             {
32473                 tag : 'div',
32474                 cls : 'column col-md-12',
32475                 cn : managerWidget
32476             }
32477         ];
32478         
32479         if(this.fieldLabel.length){
32480             
32481             content = [
32482                 {
32483                     tag : 'div',
32484                     cls : 'column col-md-12',
32485                     html : this.fieldLabel
32486                 },
32487                 {
32488                     tag : 'div',
32489                     cls : 'column col-md-12',
32490                     cn : managerWidget
32491                 }
32492             ];
32493
32494             if(this.labelAlign == 'left'){
32495                 content = [
32496                     {
32497                         tag : 'div',
32498                         cls : 'column',
32499                         html : this.fieldLabel
32500                     },
32501                     {
32502                         tag : 'div',
32503                         cls : 'column',
32504                         cn : managerWidget
32505                     }
32506                 ];
32507                 
32508                 if(this.labelWidth > 12){
32509                     content[0].style = "width: " + this.labelWidth + 'px';
32510                 }
32511
32512                 if(this.labelWidth < 13 && this.labelmd == 0){
32513                     this.labelmd = this.labelWidth;
32514                 }
32515
32516                 if(this.labellg > 0){
32517                     content[0].cls += ' col-lg-' + this.labellg;
32518                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32519                 }
32520
32521                 if(this.labelmd > 0){
32522                     content[0].cls += ' col-md-' + this.labelmd;
32523                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32524                 }
32525
32526                 if(this.labelsm > 0){
32527                     content[0].cls += ' col-sm-' + this.labelsm;
32528                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32529                 }
32530
32531                 if(this.labelxs > 0){
32532                     content[0].cls += ' col-xs-' + this.labelxs;
32533                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32534                 }
32535                 
32536             }
32537         }
32538         
32539         var cfg = {
32540             tag : 'div',
32541             cls : 'row clearfix',
32542             cn : content
32543         };
32544         
32545         return cfg;
32546         
32547     },
32548     
32549     initEvents : function()
32550     {
32551         this.managerEl = this.el.select('.roo-document-manager', true).first();
32552         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32553         
32554         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32555         this.selectorEl.hide();
32556         
32557         if(this.multiple){
32558             this.selectorEl.attr('multiple', 'multiple');
32559         }
32560         
32561         this.selectorEl.on('change', this.onFileSelected, this);
32562         
32563         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32564         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32565         
32566         this.uploader.on('click', this.onUploaderClick, this);
32567         
32568         this.renderProgressDialog();
32569         
32570         var _this = this;
32571         
32572         window.addEventListener("resize", function() { _this.refresh(); } );
32573         
32574         this.fireEvent('initial', this);
32575     },
32576     
32577     renderProgressDialog : function()
32578     {
32579         var _this = this;
32580         
32581         this.progressDialog = new Roo.bootstrap.Modal({
32582             cls : 'roo-document-manager-progress-dialog',
32583             allow_close : false,
32584             animate : false,
32585             title : '',
32586             buttons : [
32587                 {
32588                     name  :'cancel',
32589                     weight : 'danger',
32590                     html : 'Cancel'
32591                 }
32592             ], 
32593             listeners : { 
32594                 btnclick : function() {
32595                     _this.uploadCancel();
32596                     this.hide();
32597                 }
32598             }
32599         });
32600          
32601         this.progressDialog.render(Roo.get(document.body));
32602          
32603         this.progress = new Roo.bootstrap.Progress({
32604             cls : 'roo-document-manager-progress',
32605             active : true,
32606             striped : true
32607         });
32608         
32609         this.progress.render(this.progressDialog.getChildContainer());
32610         
32611         this.progressBar = new Roo.bootstrap.ProgressBar({
32612             cls : 'roo-document-manager-progress-bar',
32613             aria_valuenow : 0,
32614             aria_valuemin : 0,
32615             aria_valuemax : 12,
32616             panel : 'success'
32617         });
32618         
32619         this.progressBar.render(this.progress.getChildContainer());
32620     },
32621     
32622     onUploaderClick : function(e)
32623     {
32624         e.preventDefault();
32625      
32626         if(this.fireEvent('beforeselectfile', this) != false){
32627             this.selectorEl.dom.click();
32628         }
32629         
32630     },
32631     
32632     onFileSelected : function(e)
32633     {
32634         e.preventDefault();
32635         
32636         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32637             return;
32638         }
32639         
32640         Roo.each(this.selectorEl.dom.files, function(file){
32641             if(this.fireEvent('inspect', this, file) != false){
32642                 this.files.push(file);
32643             }
32644         }, this);
32645         
32646         this.queue();
32647         
32648     },
32649     
32650     queue : function()
32651     {
32652         this.selectorEl.dom.value = '';
32653         
32654         if(!this.files || !this.files.length){
32655             return;
32656         }
32657         
32658         if(this.boxes > 0 && this.files.length > this.boxes){
32659             this.files = this.files.slice(0, this.boxes);
32660         }
32661         
32662         this.uploader.show();
32663         
32664         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32665             this.uploader.hide();
32666         }
32667         
32668         var _this = this;
32669         
32670         var files = [];
32671         
32672         var docs = [];
32673         
32674         Roo.each(this.files, function(file){
32675             
32676             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32677                 var f = this.renderPreview(file);
32678                 files.push(f);
32679                 return;
32680             }
32681             
32682             if(file.type.indexOf('image') != -1){
32683                 this.delegates.push(
32684                     (function(){
32685                         _this.process(file);
32686                     }).createDelegate(this)
32687                 );
32688         
32689                 return;
32690             }
32691             
32692             docs.push(
32693                 (function(){
32694                     _this.process(file);
32695                 }).createDelegate(this)
32696             );
32697             
32698         }, this);
32699         
32700         this.files = files;
32701         
32702         this.delegates = this.delegates.concat(docs);
32703         
32704         if(!this.delegates.length){
32705             this.refresh();
32706             return;
32707         }
32708         
32709         this.progressBar.aria_valuemax = this.delegates.length;
32710         
32711         this.arrange();
32712         
32713         return;
32714     },
32715     
32716     arrange : function()
32717     {
32718         if(!this.delegates.length){
32719             this.progressDialog.hide();
32720             this.refresh();
32721             return;
32722         }
32723         
32724         var delegate = this.delegates.shift();
32725         
32726         this.progressDialog.show();
32727         
32728         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32729         
32730         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32731         
32732         delegate();
32733     },
32734     
32735     refresh : function()
32736     {
32737         this.uploader.show();
32738         
32739         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32740             this.uploader.hide();
32741         }
32742         
32743         Roo.isTouch ? this.closable(false) : this.closable(true);
32744         
32745         this.fireEvent('refresh', this);
32746     },
32747     
32748     onRemove : function(e, el, o)
32749     {
32750         e.preventDefault();
32751         
32752         this.fireEvent('remove', this, o);
32753         
32754     },
32755     
32756     remove : function(o)
32757     {
32758         var files = [];
32759         
32760         Roo.each(this.files, function(file){
32761             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32762                 files.push(file);
32763                 return;
32764             }
32765
32766             o.target.remove();
32767
32768         }, this);
32769         
32770         this.files = files;
32771         
32772         this.refresh();
32773     },
32774     
32775     clear : function()
32776     {
32777         Roo.each(this.files, function(file){
32778             if(!file.target){
32779                 return;
32780             }
32781             
32782             file.target.remove();
32783
32784         }, this);
32785         
32786         this.files = [];
32787         
32788         this.refresh();
32789     },
32790     
32791     onClick : function(e, el, o)
32792     {
32793         e.preventDefault();
32794         
32795         this.fireEvent('click', this, o);
32796         
32797     },
32798     
32799     closable : function(closable)
32800     {
32801         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32802             
32803             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32804             
32805             if(closable){
32806                 el.show();
32807                 return;
32808             }
32809             
32810             el.hide();
32811             
32812         }, this);
32813     },
32814     
32815     xhrOnLoad : function(xhr)
32816     {
32817         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32818             el.remove();
32819         }, this);
32820         
32821         if (xhr.readyState !== 4) {
32822             this.arrange();
32823             this.fireEvent('exception', this, xhr);
32824             return;
32825         }
32826
32827         var response = Roo.decode(xhr.responseText);
32828         
32829         if(!response.success){
32830             this.arrange();
32831             this.fireEvent('exception', this, xhr);
32832             return;
32833         }
32834         
32835         var file = this.renderPreview(response.data);
32836         
32837         this.files.push(file);
32838         
32839         this.arrange();
32840         
32841         this.fireEvent('afterupload', this, xhr);
32842         
32843     },
32844     
32845     xhrOnError : function(xhr)
32846     {
32847         Roo.log('xhr on error');
32848         
32849         var response = Roo.decode(xhr.responseText);
32850           
32851         Roo.log(response);
32852         
32853         this.arrange();
32854     },
32855     
32856     process : function(file)
32857     {
32858         if(this.fireEvent('process', this, file) !== false){
32859             if(this.editable && file.type.indexOf('image') != -1){
32860                 this.fireEvent('edit', this, file);
32861                 return;
32862             }
32863
32864             this.uploadStart(file, false);
32865
32866             return;
32867         }
32868         
32869     },
32870     
32871     uploadStart : function(file, crop)
32872     {
32873         this.xhr = new XMLHttpRequest();
32874         
32875         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32876             this.arrange();
32877             return;
32878         }
32879         
32880         file.xhr = this.xhr;
32881             
32882         this.managerEl.createChild({
32883             tag : 'div',
32884             cls : 'roo-document-manager-loading',
32885             cn : [
32886                 {
32887                     tag : 'div',
32888                     tooltip : file.name,
32889                     cls : 'roo-document-manager-thumb',
32890                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32891                 }
32892             ]
32893
32894         });
32895
32896         this.xhr.open(this.method, this.url, true);
32897         
32898         var headers = {
32899             "Accept": "application/json",
32900             "Cache-Control": "no-cache",
32901             "X-Requested-With": "XMLHttpRequest"
32902         };
32903         
32904         for (var headerName in headers) {
32905             var headerValue = headers[headerName];
32906             if (headerValue) {
32907                 this.xhr.setRequestHeader(headerName, headerValue);
32908             }
32909         }
32910         
32911         var _this = this;
32912         
32913         this.xhr.onload = function()
32914         {
32915             _this.xhrOnLoad(_this.xhr);
32916         }
32917         
32918         this.xhr.onerror = function()
32919         {
32920             _this.xhrOnError(_this.xhr);
32921         }
32922         
32923         var formData = new FormData();
32924
32925         formData.append('returnHTML', 'NO');
32926         
32927         if(crop){
32928             formData.append('crop', crop);
32929         }
32930         
32931         formData.append(this.paramName, file, file.name);
32932         
32933         var options = {
32934             file : file, 
32935             manually : false
32936         };
32937         
32938         if(this.fireEvent('prepare', this, formData, options) != false){
32939             
32940             if(options.manually){
32941                 return;
32942             }
32943             
32944             this.xhr.send(formData);
32945             return;
32946         };
32947         
32948         this.uploadCancel();
32949     },
32950     
32951     uploadCancel : function()
32952     {
32953         if (this.xhr) {
32954             this.xhr.abort();
32955         }
32956         
32957         this.delegates = [];
32958         
32959         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32960             el.remove();
32961         }, this);
32962         
32963         this.arrange();
32964     },
32965     
32966     renderPreview : function(file)
32967     {
32968         if(typeof(file.target) != 'undefined' && file.target){
32969             return file;
32970         }
32971         
32972         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32973         
32974         var previewEl = this.managerEl.createChild({
32975             tag : 'div',
32976             cls : 'roo-document-manager-preview',
32977             cn : [
32978                 {
32979                     tag : 'div',
32980                     tooltip : file[this.toolTipName],
32981                     cls : 'roo-document-manager-thumb',
32982                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32983                 },
32984                 {
32985                     tag : 'button',
32986                     cls : 'close',
32987                     html : '<i class="fa fa-times-circle"></i>'
32988                 }
32989             ]
32990         });
32991
32992         var close = previewEl.select('button.close', true).first();
32993
32994         close.on('click', this.onRemove, this, file);
32995
32996         file.target = previewEl;
32997
32998         var image = previewEl.select('img', true).first();
32999         
33000         var _this = this;
33001         
33002         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33003         
33004         image.on('click', this.onClick, this, file);
33005         
33006         this.fireEvent('previewrendered', this, file);
33007         
33008         return file;
33009         
33010     },
33011     
33012     onPreviewLoad : function(file, image)
33013     {
33014         if(typeof(file.target) == 'undefined' || !file.target){
33015             return;
33016         }
33017         
33018         var width = image.dom.naturalWidth || image.dom.width;
33019         var height = image.dom.naturalHeight || image.dom.height;
33020         
33021         if(!this.previewResize) {
33022             return;
33023         }
33024         
33025         if(width > height){
33026             file.target.addClass('wide');
33027             return;
33028         }
33029         
33030         file.target.addClass('tall');
33031         return;
33032         
33033     },
33034     
33035     uploadFromSource : function(file, crop)
33036     {
33037         this.xhr = new XMLHttpRequest();
33038         
33039         this.managerEl.createChild({
33040             tag : 'div',
33041             cls : 'roo-document-manager-loading',
33042             cn : [
33043                 {
33044                     tag : 'div',
33045                     tooltip : file.name,
33046                     cls : 'roo-document-manager-thumb',
33047                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33048                 }
33049             ]
33050
33051         });
33052
33053         this.xhr.open(this.method, this.url, true);
33054         
33055         var headers = {
33056             "Accept": "application/json",
33057             "Cache-Control": "no-cache",
33058             "X-Requested-With": "XMLHttpRequest"
33059         };
33060         
33061         for (var headerName in headers) {
33062             var headerValue = headers[headerName];
33063             if (headerValue) {
33064                 this.xhr.setRequestHeader(headerName, headerValue);
33065             }
33066         }
33067         
33068         var _this = this;
33069         
33070         this.xhr.onload = function()
33071         {
33072             _this.xhrOnLoad(_this.xhr);
33073         }
33074         
33075         this.xhr.onerror = function()
33076         {
33077             _this.xhrOnError(_this.xhr);
33078         }
33079         
33080         var formData = new FormData();
33081
33082         formData.append('returnHTML', 'NO');
33083         
33084         formData.append('crop', crop);
33085         
33086         if(typeof(file.filename) != 'undefined'){
33087             formData.append('filename', file.filename);
33088         }
33089         
33090         if(typeof(file.mimetype) != 'undefined'){
33091             formData.append('mimetype', file.mimetype);
33092         }
33093         
33094         Roo.log(formData);
33095         
33096         if(this.fireEvent('prepare', this, formData) != false){
33097             this.xhr.send(formData);
33098         };
33099     }
33100 });
33101
33102 /*
33103 * Licence: LGPL
33104 */
33105
33106 /**
33107  * @class Roo.bootstrap.DocumentViewer
33108  * @extends Roo.bootstrap.Component
33109  * Bootstrap DocumentViewer class
33110  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33111  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33112  * 
33113  * @constructor
33114  * Create a new DocumentViewer
33115  * @param {Object} config The config object
33116  */
33117
33118 Roo.bootstrap.DocumentViewer = function(config){
33119     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33120     
33121     this.addEvents({
33122         /**
33123          * @event initial
33124          * Fire after initEvent
33125          * @param {Roo.bootstrap.DocumentViewer} this
33126          */
33127         "initial" : true,
33128         /**
33129          * @event click
33130          * Fire after click
33131          * @param {Roo.bootstrap.DocumentViewer} this
33132          */
33133         "click" : true,
33134         /**
33135          * @event download
33136          * Fire after download button
33137          * @param {Roo.bootstrap.DocumentViewer} this
33138          */
33139         "download" : true,
33140         /**
33141          * @event trash
33142          * Fire after trash button
33143          * @param {Roo.bootstrap.DocumentViewer} this
33144          */
33145         "trash" : true
33146         
33147     });
33148 };
33149
33150 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33151     
33152     showDownload : true,
33153     
33154     showTrash : true,
33155     
33156     getAutoCreate : function()
33157     {
33158         var cfg = {
33159             tag : 'div',
33160             cls : 'roo-document-viewer',
33161             cn : [
33162                 {
33163                     tag : 'div',
33164                     cls : 'roo-document-viewer-body',
33165                     cn : [
33166                         {
33167                             tag : 'div',
33168                             cls : 'roo-document-viewer-thumb',
33169                             cn : [
33170                                 {
33171                                     tag : 'img',
33172                                     cls : 'roo-document-viewer-image'
33173                                 }
33174                             ]
33175                         }
33176                     ]
33177                 },
33178                 {
33179                     tag : 'div',
33180                     cls : 'roo-document-viewer-footer',
33181                     cn : {
33182                         tag : 'div',
33183                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33184                         cn : [
33185                             {
33186                                 tag : 'div',
33187                                 cls : 'btn-group roo-document-viewer-download',
33188                                 cn : [
33189                                     {
33190                                         tag : 'button',
33191                                         cls : 'btn btn-default',
33192                                         html : '<i class="fa fa-download"></i>'
33193                                     }
33194                                 ]
33195                             },
33196                             {
33197                                 tag : 'div',
33198                                 cls : 'btn-group roo-document-viewer-trash',
33199                                 cn : [
33200                                     {
33201                                         tag : 'button',
33202                                         cls : 'btn btn-default',
33203                                         html : '<i class="fa fa-trash"></i>'
33204                                     }
33205                                 ]
33206                             }
33207                         ]
33208                     }
33209                 }
33210             ]
33211         };
33212         
33213         return cfg;
33214     },
33215     
33216     initEvents : function()
33217     {
33218         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33219         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33220         
33221         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33222         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33223         
33224         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33225         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33226         
33227         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33228         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33229         
33230         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33231         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33232         
33233         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33234         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33235         
33236         this.bodyEl.on('click', this.onClick, this);
33237         this.downloadBtn.on('click', this.onDownload, this);
33238         this.trashBtn.on('click', this.onTrash, this);
33239         
33240         this.downloadBtn.hide();
33241         this.trashBtn.hide();
33242         
33243         if(this.showDownload){
33244             this.downloadBtn.show();
33245         }
33246         
33247         if(this.showTrash){
33248             this.trashBtn.show();
33249         }
33250         
33251         if(!this.showDownload && !this.showTrash) {
33252             this.footerEl.hide();
33253         }
33254         
33255     },
33256     
33257     initial : function()
33258     {
33259         this.fireEvent('initial', this);
33260         
33261     },
33262     
33263     onClick : function(e)
33264     {
33265         e.preventDefault();
33266         
33267         this.fireEvent('click', this);
33268     },
33269     
33270     onDownload : function(e)
33271     {
33272         e.preventDefault();
33273         
33274         this.fireEvent('download', this);
33275     },
33276     
33277     onTrash : function(e)
33278     {
33279         e.preventDefault();
33280         
33281         this.fireEvent('trash', this);
33282     }
33283     
33284 });
33285 /*
33286  * - LGPL
33287  *
33288  * nav progress bar
33289  * 
33290  */
33291
33292 /**
33293  * @class Roo.bootstrap.NavProgressBar
33294  * @extends Roo.bootstrap.Component
33295  * Bootstrap NavProgressBar class
33296  * 
33297  * @constructor
33298  * Create a new nav progress bar
33299  * @param {Object} config The config object
33300  */
33301
33302 Roo.bootstrap.NavProgressBar = function(config){
33303     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33304
33305     this.bullets = this.bullets || [];
33306    
33307 //    Roo.bootstrap.NavProgressBar.register(this);
33308      this.addEvents({
33309         /**
33310              * @event changed
33311              * Fires when the active item changes
33312              * @param {Roo.bootstrap.NavProgressBar} this
33313              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33314              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33315          */
33316         'changed': true
33317      });
33318     
33319 };
33320
33321 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33322     
33323     bullets : [],
33324     barItems : [],
33325     
33326     getAutoCreate : function()
33327     {
33328         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33329         
33330         cfg = {
33331             tag : 'div',
33332             cls : 'roo-navigation-bar-group',
33333             cn : [
33334                 {
33335                     tag : 'div',
33336                     cls : 'roo-navigation-top-bar'
33337                 },
33338                 {
33339                     tag : 'div',
33340                     cls : 'roo-navigation-bullets-bar',
33341                     cn : [
33342                         {
33343                             tag : 'ul',
33344                             cls : 'roo-navigation-bar'
33345                         }
33346                     ]
33347                 },
33348                 
33349                 {
33350                     tag : 'div',
33351                     cls : 'roo-navigation-bottom-bar'
33352                 }
33353             ]
33354             
33355         };
33356         
33357         return cfg;
33358         
33359     },
33360     
33361     initEvents: function() 
33362     {
33363         
33364     },
33365     
33366     onRender : function(ct, position) 
33367     {
33368         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33369         
33370         if(this.bullets.length){
33371             Roo.each(this.bullets, function(b){
33372                this.addItem(b);
33373             }, this);
33374         }
33375         
33376         this.format();
33377         
33378     },
33379     
33380     addItem : function(cfg)
33381     {
33382         var item = new Roo.bootstrap.NavProgressItem(cfg);
33383         
33384         item.parentId = this.id;
33385         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33386         
33387         if(cfg.html){
33388             var top = new Roo.bootstrap.Element({
33389                 tag : 'div',
33390                 cls : 'roo-navigation-bar-text'
33391             });
33392             
33393             var bottom = new Roo.bootstrap.Element({
33394                 tag : 'div',
33395                 cls : 'roo-navigation-bar-text'
33396             });
33397             
33398             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33399             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33400             
33401             var topText = new Roo.bootstrap.Element({
33402                 tag : 'span',
33403                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33404             });
33405             
33406             var bottomText = new Roo.bootstrap.Element({
33407                 tag : 'span',
33408                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33409             });
33410             
33411             topText.onRender(top.el, null);
33412             bottomText.onRender(bottom.el, null);
33413             
33414             item.topEl = top;
33415             item.bottomEl = bottom;
33416         }
33417         
33418         this.barItems.push(item);
33419         
33420         return item;
33421     },
33422     
33423     getActive : function()
33424     {
33425         var active = false;
33426         
33427         Roo.each(this.barItems, function(v){
33428             
33429             if (!v.isActive()) {
33430                 return;
33431             }
33432             
33433             active = v;
33434             return false;
33435             
33436         });
33437         
33438         return active;
33439     },
33440     
33441     setActiveItem : function(item)
33442     {
33443         var prev = false;
33444         
33445         Roo.each(this.barItems, function(v){
33446             if (v.rid == item.rid) {
33447                 return ;
33448             }
33449             
33450             if (v.isActive()) {
33451                 v.setActive(false);
33452                 prev = v;
33453             }
33454         });
33455
33456         item.setActive(true);
33457         
33458         this.fireEvent('changed', this, item, prev);
33459     },
33460     
33461     getBarItem: function(rid)
33462     {
33463         var ret = false;
33464         
33465         Roo.each(this.barItems, function(e) {
33466             if (e.rid != rid) {
33467                 return;
33468             }
33469             
33470             ret =  e;
33471             return false;
33472         });
33473         
33474         return ret;
33475     },
33476     
33477     indexOfItem : function(item)
33478     {
33479         var index = false;
33480         
33481         Roo.each(this.barItems, function(v, i){
33482             
33483             if (v.rid != item.rid) {
33484                 return;
33485             }
33486             
33487             index = i;
33488             return false
33489         });
33490         
33491         return index;
33492     },
33493     
33494     setActiveNext : function()
33495     {
33496         var i = this.indexOfItem(this.getActive());
33497         
33498         if (i > this.barItems.length) {
33499             return;
33500         }
33501         
33502         this.setActiveItem(this.barItems[i+1]);
33503     },
33504     
33505     setActivePrev : function()
33506     {
33507         var i = this.indexOfItem(this.getActive());
33508         
33509         if (i  < 1) {
33510             return;
33511         }
33512         
33513         this.setActiveItem(this.barItems[i-1]);
33514     },
33515     
33516     format : function()
33517     {
33518         if(!this.barItems.length){
33519             return;
33520         }
33521      
33522         var width = 100 / this.barItems.length;
33523         
33524         Roo.each(this.barItems, function(i){
33525             i.el.setStyle('width', width + '%');
33526             i.topEl.el.setStyle('width', width + '%');
33527             i.bottomEl.el.setStyle('width', width + '%');
33528         }, this);
33529         
33530     }
33531     
33532 });
33533 /*
33534  * - LGPL
33535  *
33536  * Nav Progress Item
33537  * 
33538  */
33539
33540 /**
33541  * @class Roo.bootstrap.NavProgressItem
33542  * @extends Roo.bootstrap.Component
33543  * Bootstrap NavProgressItem class
33544  * @cfg {String} rid the reference id
33545  * @cfg {Boolean} active (true|false) Is item active default false
33546  * @cfg {Boolean} disabled (true|false) Is item active default false
33547  * @cfg {String} html
33548  * @cfg {String} position (top|bottom) text position default bottom
33549  * @cfg {String} icon show icon instead of number
33550  * 
33551  * @constructor
33552  * Create a new NavProgressItem
33553  * @param {Object} config The config object
33554  */
33555 Roo.bootstrap.NavProgressItem = function(config){
33556     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33557     this.addEvents({
33558         // raw events
33559         /**
33560          * @event click
33561          * The raw click event for the entire grid.
33562          * @param {Roo.bootstrap.NavProgressItem} this
33563          * @param {Roo.EventObject} e
33564          */
33565         "click" : true
33566     });
33567    
33568 };
33569
33570 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33571     
33572     rid : '',
33573     active : false,
33574     disabled : false,
33575     html : '',
33576     position : 'bottom',
33577     icon : false,
33578     
33579     getAutoCreate : function()
33580     {
33581         var iconCls = 'roo-navigation-bar-item-icon';
33582         
33583         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33584         
33585         var cfg = {
33586             tag: 'li',
33587             cls: 'roo-navigation-bar-item',
33588             cn : [
33589                 {
33590                     tag : 'i',
33591                     cls : iconCls
33592                 }
33593             ]
33594         };
33595         
33596         if(this.active){
33597             cfg.cls += ' active';
33598         }
33599         if(this.disabled){
33600             cfg.cls += ' disabled';
33601         }
33602         
33603         return cfg;
33604     },
33605     
33606     disable : function()
33607     {
33608         this.setDisabled(true);
33609     },
33610     
33611     enable : function()
33612     {
33613         this.setDisabled(false);
33614     },
33615     
33616     initEvents: function() 
33617     {
33618         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33619         
33620         this.iconEl.on('click', this.onClick, this);
33621     },
33622     
33623     onClick : function(e)
33624     {
33625         e.preventDefault();
33626         
33627         if(this.disabled){
33628             return;
33629         }
33630         
33631         if(this.fireEvent('click', this, e) === false){
33632             return;
33633         };
33634         
33635         this.parent().setActiveItem(this);
33636     },
33637     
33638     isActive: function () 
33639     {
33640         return this.active;
33641     },
33642     
33643     setActive : function(state)
33644     {
33645         if(this.active == state){
33646             return;
33647         }
33648         
33649         this.active = state;
33650         
33651         if (state) {
33652             this.el.addClass('active');
33653             return;
33654         }
33655         
33656         this.el.removeClass('active');
33657         
33658         return;
33659     },
33660     
33661     setDisabled : function(state)
33662     {
33663         if(this.disabled == state){
33664             return;
33665         }
33666         
33667         this.disabled = state;
33668         
33669         if (state) {
33670             this.el.addClass('disabled');
33671             return;
33672         }
33673         
33674         this.el.removeClass('disabled');
33675     },
33676     
33677     tooltipEl : function()
33678     {
33679         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33680     }
33681 });
33682  
33683
33684  /*
33685  * - LGPL
33686  *
33687  * FieldLabel
33688  * 
33689  */
33690
33691 /**
33692  * @class Roo.bootstrap.FieldLabel
33693  * @extends Roo.bootstrap.Component
33694  * Bootstrap FieldLabel class
33695  * @cfg {String} html contents of the element
33696  * @cfg {String} tag tag of the element default label
33697  * @cfg {String} cls class of the element
33698  * @cfg {String} target label target 
33699  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33700  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33701  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33702  * @cfg {String} iconTooltip default "This field is required"
33703  * @cfg {String} indicatorpos (left|right) default left
33704  * 
33705  * @constructor
33706  * Create a new FieldLabel
33707  * @param {Object} config The config object
33708  */
33709
33710 Roo.bootstrap.FieldLabel = function(config){
33711     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33712     
33713     this.addEvents({
33714             /**
33715              * @event invalid
33716              * Fires after the field has been marked as invalid.
33717              * @param {Roo.form.FieldLabel} this
33718              * @param {String} msg The validation message
33719              */
33720             invalid : true,
33721             /**
33722              * @event valid
33723              * Fires after the field has been validated with no errors.
33724              * @param {Roo.form.FieldLabel} this
33725              */
33726             valid : true
33727         });
33728 };
33729
33730 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33731     
33732     tag: 'label',
33733     cls: '',
33734     html: '',
33735     target: '',
33736     allowBlank : true,
33737     invalidClass : 'has-warning',
33738     validClass : 'has-success',
33739     iconTooltip : 'This field is required',
33740     indicatorpos : 'left',
33741     
33742     getAutoCreate : function(){
33743         
33744         var cls = "";
33745         if (!this.allowBlank) {
33746             cls  = "visible";
33747         }
33748         
33749         var cfg = {
33750             tag : this.tag,
33751             cls : 'roo-bootstrap-field-label ' + this.cls,
33752             for : this.target,
33753             cn : [
33754                 {
33755                     tag : 'i',
33756                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33757                     tooltip : this.iconTooltip
33758                 },
33759                 {
33760                     tag : 'span',
33761                     html : this.html
33762                 }
33763             ] 
33764         };
33765         
33766         if(this.indicatorpos == 'right'){
33767             var cfg = {
33768                 tag : this.tag,
33769                 cls : 'roo-bootstrap-field-label ' + this.cls,
33770                 for : this.target,
33771                 cn : [
33772                     {
33773                         tag : 'span',
33774                         html : this.html
33775                     },
33776                     {
33777                         tag : 'i',
33778                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33779                         tooltip : this.iconTooltip
33780                     }
33781                 ] 
33782             };
33783         }
33784         
33785         return cfg;
33786     },
33787     
33788     initEvents: function() 
33789     {
33790         Roo.bootstrap.Element.superclass.initEvents.call(this);
33791         
33792         this.indicator = this.indicatorEl();
33793         
33794         if(this.indicator){
33795             this.indicator.removeClass('visible');
33796             this.indicator.addClass('invisible');
33797         }
33798         
33799         Roo.bootstrap.FieldLabel.register(this);
33800     },
33801     
33802     indicatorEl : function()
33803     {
33804         var indicator = this.el.select('i.roo-required-indicator',true).first();
33805         
33806         if(!indicator){
33807             return false;
33808         }
33809         
33810         return indicator;
33811         
33812     },
33813     
33814     /**
33815      * Mark this field as valid
33816      */
33817     markValid : function()
33818     {
33819         if(this.indicator){
33820             this.indicator.removeClass('visible');
33821             this.indicator.addClass('invisible');
33822         }
33823         if (Roo.bootstrap.version == 3) {
33824             this.el.removeClass(this.invalidClass);
33825             this.el.addClass(this.validClass);
33826         } else {
33827             this.el.removeClass('is-invalid');
33828             this.el.addClass('is-valid');
33829         }
33830         
33831         
33832         this.fireEvent('valid', this);
33833     },
33834     
33835     /**
33836      * Mark this field as invalid
33837      * @param {String} msg The validation message
33838      */
33839     markInvalid : function(msg)
33840     {
33841         if(this.indicator){
33842             this.indicator.removeClass('invisible');
33843             this.indicator.addClass('visible');
33844         }
33845           if (Roo.bootstrap.version == 3) {
33846             this.el.removeClass(this.validClass);
33847             this.el.addClass(this.invalidClass);
33848         } else {
33849             this.el.removeClass('is-valid');
33850             this.el.addClass('is-invalid');
33851         }
33852         
33853         
33854         this.fireEvent('invalid', this, msg);
33855     }
33856     
33857    
33858 });
33859
33860 Roo.apply(Roo.bootstrap.FieldLabel, {
33861     
33862     groups: {},
33863     
33864      /**
33865     * register a FieldLabel Group
33866     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33867     */
33868     register : function(label)
33869     {
33870         if(this.groups.hasOwnProperty(label.target)){
33871             return;
33872         }
33873      
33874         this.groups[label.target] = label;
33875         
33876     },
33877     /**
33878     * fetch a FieldLabel Group based on the target
33879     * @param {string} target
33880     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33881     */
33882     get: function(target) {
33883         if (typeof(this.groups[target]) == 'undefined') {
33884             return false;
33885         }
33886         
33887         return this.groups[target] ;
33888     }
33889 });
33890
33891  
33892
33893  /*
33894  * - LGPL
33895  *
33896  * page DateSplitField.
33897  * 
33898  */
33899
33900
33901 /**
33902  * @class Roo.bootstrap.DateSplitField
33903  * @extends Roo.bootstrap.Component
33904  * Bootstrap DateSplitField class
33905  * @cfg {string} fieldLabel - the label associated
33906  * @cfg {Number} labelWidth set the width of label (0-12)
33907  * @cfg {String} labelAlign (top|left)
33908  * @cfg {Boolean} dayAllowBlank (true|false) default false
33909  * @cfg {Boolean} monthAllowBlank (true|false) default false
33910  * @cfg {Boolean} yearAllowBlank (true|false) default false
33911  * @cfg {string} dayPlaceholder 
33912  * @cfg {string} monthPlaceholder
33913  * @cfg {string} yearPlaceholder
33914  * @cfg {string} dayFormat default 'd'
33915  * @cfg {string} monthFormat default 'm'
33916  * @cfg {string} yearFormat default 'Y'
33917  * @cfg {Number} labellg set the width of label (1-12)
33918  * @cfg {Number} labelmd set the width of label (1-12)
33919  * @cfg {Number} labelsm set the width of label (1-12)
33920  * @cfg {Number} labelxs set the width of label (1-12)
33921
33922  *     
33923  * @constructor
33924  * Create a new DateSplitField
33925  * @param {Object} config The config object
33926  */
33927
33928 Roo.bootstrap.DateSplitField = function(config){
33929     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33930     
33931     this.addEvents({
33932         // raw events
33933          /**
33934          * @event years
33935          * getting the data of years
33936          * @param {Roo.bootstrap.DateSplitField} this
33937          * @param {Object} years
33938          */
33939         "years" : true,
33940         /**
33941          * @event days
33942          * getting the data of days
33943          * @param {Roo.bootstrap.DateSplitField} this
33944          * @param {Object} days
33945          */
33946         "days" : true,
33947         /**
33948          * @event invalid
33949          * Fires after the field has been marked as invalid.
33950          * @param {Roo.form.Field} this
33951          * @param {String} msg The validation message
33952          */
33953         invalid : true,
33954        /**
33955          * @event valid
33956          * Fires after the field has been validated with no errors.
33957          * @param {Roo.form.Field} this
33958          */
33959         valid : true
33960     });
33961 };
33962
33963 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33964     
33965     fieldLabel : '',
33966     labelAlign : 'top',
33967     labelWidth : 3,
33968     dayAllowBlank : false,
33969     monthAllowBlank : false,
33970     yearAllowBlank : false,
33971     dayPlaceholder : '',
33972     monthPlaceholder : '',
33973     yearPlaceholder : '',
33974     dayFormat : 'd',
33975     monthFormat : 'm',
33976     yearFormat : 'Y',
33977     isFormField : true,
33978     labellg : 0,
33979     labelmd : 0,
33980     labelsm : 0,
33981     labelxs : 0,
33982     
33983     getAutoCreate : function()
33984     {
33985         var cfg = {
33986             tag : 'div',
33987             cls : 'row roo-date-split-field-group',
33988             cn : [
33989                 {
33990                     tag : 'input',
33991                     type : 'hidden',
33992                     cls : 'form-hidden-field roo-date-split-field-group-value',
33993                     name : this.name
33994                 }
33995             ]
33996         };
33997         
33998         var labelCls = 'col-md-12';
33999         var contentCls = 'col-md-4';
34000         
34001         if(this.fieldLabel){
34002             
34003             var label = {
34004                 tag : 'div',
34005                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34006                 cn : [
34007                     {
34008                         tag : 'label',
34009                         html : this.fieldLabel
34010                     }
34011                 ]
34012             };
34013             
34014             if(this.labelAlign == 'left'){
34015             
34016                 if(this.labelWidth > 12){
34017                     label.style = "width: " + this.labelWidth + 'px';
34018                 }
34019
34020                 if(this.labelWidth < 13 && this.labelmd == 0){
34021                     this.labelmd = this.labelWidth;
34022                 }
34023
34024                 if(this.labellg > 0){
34025                     labelCls = ' col-lg-' + this.labellg;
34026                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34027                 }
34028
34029                 if(this.labelmd > 0){
34030                     labelCls = ' col-md-' + this.labelmd;
34031                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34032                 }
34033
34034                 if(this.labelsm > 0){
34035                     labelCls = ' col-sm-' + this.labelsm;
34036                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34037                 }
34038
34039                 if(this.labelxs > 0){
34040                     labelCls = ' col-xs-' + this.labelxs;
34041                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34042                 }
34043             }
34044             
34045             label.cls += ' ' + labelCls;
34046             
34047             cfg.cn.push(label);
34048         }
34049         
34050         Roo.each(['day', 'month', 'year'], function(t){
34051             cfg.cn.push({
34052                 tag : 'div',
34053                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34054             });
34055         }, this);
34056         
34057         return cfg;
34058     },
34059     
34060     inputEl: function ()
34061     {
34062         return this.el.select('.roo-date-split-field-group-value', true).first();
34063     },
34064     
34065     onRender : function(ct, position) 
34066     {
34067         var _this = this;
34068         
34069         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34070         
34071         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34072         
34073         this.dayField = new Roo.bootstrap.ComboBox({
34074             allowBlank : this.dayAllowBlank,
34075             alwaysQuery : true,
34076             displayField : 'value',
34077             editable : false,
34078             fieldLabel : '',
34079             forceSelection : true,
34080             mode : 'local',
34081             placeholder : this.dayPlaceholder,
34082             selectOnFocus : true,
34083             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34084             triggerAction : 'all',
34085             typeAhead : true,
34086             valueField : 'value',
34087             store : new Roo.data.SimpleStore({
34088                 data : (function() {    
34089                     var days = [];
34090                     _this.fireEvent('days', _this, days);
34091                     return days;
34092                 })(),
34093                 fields : [ 'value' ]
34094             }),
34095             listeners : {
34096                 select : function (_self, record, index)
34097                 {
34098                     _this.setValue(_this.getValue());
34099                 }
34100             }
34101         });
34102
34103         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34104         
34105         this.monthField = new Roo.bootstrap.MonthField({
34106             after : '<i class=\"fa fa-calendar\"></i>',
34107             allowBlank : this.monthAllowBlank,
34108             placeholder : this.monthPlaceholder,
34109             readOnly : true,
34110             listeners : {
34111                 render : function (_self)
34112                 {
34113                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34114                         e.preventDefault();
34115                         _self.focus();
34116                     });
34117                 },
34118                 select : function (_self, oldvalue, newvalue)
34119                 {
34120                     _this.setValue(_this.getValue());
34121                 }
34122             }
34123         });
34124         
34125         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34126         
34127         this.yearField = new Roo.bootstrap.ComboBox({
34128             allowBlank : this.yearAllowBlank,
34129             alwaysQuery : true,
34130             displayField : 'value',
34131             editable : false,
34132             fieldLabel : '',
34133             forceSelection : true,
34134             mode : 'local',
34135             placeholder : this.yearPlaceholder,
34136             selectOnFocus : true,
34137             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34138             triggerAction : 'all',
34139             typeAhead : true,
34140             valueField : 'value',
34141             store : new Roo.data.SimpleStore({
34142                 data : (function() {
34143                     var years = [];
34144                     _this.fireEvent('years', _this, years);
34145                     return years;
34146                 })(),
34147                 fields : [ 'value' ]
34148             }),
34149             listeners : {
34150                 select : function (_self, record, index)
34151                 {
34152                     _this.setValue(_this.getValue());
34153                 }
34154             }
34155         });
34156
34157         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34158     },
34159     
34160     setValue : function(v, format)
34161     {
34162         this.inputEl.dom.value = v;
34163         
34164         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34165         
34166         var d = Date.parseDate(v, f);
34167         
34168         if(!d){
34169             this.validate();
34170             return;
34171         }
34172         
34173         this.setDay(d.format(this.dayFormat));
34174         this.setMonth(d.format(this.monthFormat));
34175         this.setYear(d.format(this.yearFormat));
34176         
34177         this.validate();
34178         
34179         return;
34180     },
34181     
34182     setDay : function(v)
34183     {
34184         this.dayField.setValue(v);
34185         this.inputEl.dom.value = this.getValue();
34186         this.validate();
34187         return;
34188     },
34189     
34190     setMonth : function(v)
34191     {
34192         this.monthField.setValue(v, true);
34193         this.inputEl.dom.value = this.getValue();
34194         this.validate();
34195         return;
34196     },
34197     
34198     setYear : function(v)
34199     {
34200         this.yearField.setValue(v);
34201         this.inputEl.dom.value = this.getValue();
34202         this.validate();
34203         return;
34204     },
34205     
34206     getDay : function()
34207     {
34208         return this.dayField.getValue();
34209     },
34210     
34211     getMonth : function()
34212     {
34213         return this.monthField.getValue();
34214     },
34215     
34216     getYear : function()
34217     {
34218         return this.yearField.getValue();
34219     },
34220     
34221     getValue : function()
34222     {
34223         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34224         
34225         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34226         
34227         return date;
34228     },
34229     
34230     reset : function()
34231     {
34232         this.setDay('');
34233         this.setMonth('');
34234         this.setYear('');
34235         this.inputEl.dom.value = '';
34236         this.validate();
34237         return;
34238     },
34239     
34240     validate : function()
34241     {
34242         var d = this.dayField.validate();
34243         var m = this.monthField.validate();
34244         var y = this.yearField.validate();
34245         
34246         var valid = true;
34247         
34248         if(
34249                 (!this.dayAllowBlank && !d) ||
34250                 (!this.monthAllowBlank && !m) ||
34251                 (!this.yearAllowBlank && !y)
34252         ){
34253             valid = false;
34254         }
34255         
34256         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34257             return valid;
34258         }
34259         
34260         if(valid){
34261             this.markValid();
34262             return valid;
34263         }
34264         
34265         this.markInvalid();
34266         
34267         return valid;
34268     },
34269     
34270     markValid : function()
34271     {
34272         
34273         var label = this.el.select('label', true).first();
34274         var icon = this.el.select('i.fa-star', true).first();
34275
34276         if(label && icon){
34277             icon.remove();
34278         }
34279         
34280         this.fireEvent('valid', this);
34281     },
34282     
34283      /**
34284      * Mark this field as invalid
34285      * @param {String} msg The validation message
34286      */
34287     markInvalid : function(msg)
34288     {
34289         
34290         var label = this.el.select('label', true).first();
34291         var icon = this.el.select('i.fa-star', true).first();
34292
34293         if(label && !icon){
34294             this.el.select('.roo-date-split-field-label', true).createChild({
34295                 tag : 'i',
34296                 cls : 'text-danger fa fa-lg fa-star',
34297                 tooltip : 'This field is required',
34298                 style : 'margin-right:5px;'
34299             }, label, true);
34300         }
34301         
34302         this.fireEvent('invalid', this, msg);
34303     },
34304     
34305     clearInvalid : function()
34306     {
34307         var label = this.el.select('label', true).first();
34308         var icon = this.el.select('i.fa-star', true).first();
34309
34310         if(label && icon){
34311             icon.remove();
34312         }
34313         
34314         this.fireEvent('valid', this);
34315     },
34316     
34317     getName: function()
34318     {
34319         return this.name;
34320     }
34321     
34322 });
34323
34324  /**
34325  *
34326  * This is based on 
34327  * http://masonry.desandro.com
34328  *
34329  * The idea is to render all the bricks based on vertical width...
34330  *
34331  * The original code extends 'outlayer' - we might need to use that....
34332  * 
34333  */
34334
34335
34336 /**
34337  * @class Roo.bootstrap.LayoutMasonry
34338  * @extends Roo.bootstrap.Component
34339  * Bootstrap Layout Masonry class
34340  * 
34341  * @constructor
34342  * Create a new Element
34343  * @param {Object} config The config object
34344  */
34345
34346 Roo.bootstrap.LayoutMasonry = function(config){
34347     
34348     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34349     
34350     this.bricks = [];
34351     
34352     Roo.bootstrap.LayoutMasonry.register(this);
34353     
34354     this.addEvents({
34355         // raw events
34356         /**
34357          * @event layout
34358          * Fire after layout the items
34359          * @param {Roo.bootstrap.LayoutMasonry} this
34360          * @param {Roo.EventObject} e
34361          */
34362         "layout" : true
34363     });
34364     
34365 };
34366
34367 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34368     
34369     /**
34370      * @cfg {Boolean} isLayoutInstant = no animation?
34371      */   
34372     isLayoutInstant : false, // needed?
34373    
34374     /**
34375      * @cfg {Number} boxWidth  width of the columns
34376      */   
34377     boxWidth : 450,
34378     
34379       /**
34380      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34381      */   
34382     boxHeight : 0,
34383     
34384     /**
34385      * @cfg {Number} padWidth padding below box..
34386      */   
34387     padWidth : 10, 
34388     
34389     /**
34390      * @cfg {Number} gutter gutter width..
34391      */   
34392     gutter : 10,
34393     
34394      /**
34395      * @cfg {Number} maxCols maximum number of columns
34396      */   
34397     
34398     maxCols: 0,
34399     
34400     /**
34401      * @cfg {Boolean} isAutoInitial defalut true
34402      */   
34403     isAutoInitial : true, 
34404     
34405     containerWidth: 0,
34406     
34407     /**
34408      * @cfg {Boolean} isHorizontal defalut false
34409      */   
34410     isHorizontal : false, 
34411
34412     currentSize : null,
34413     
34414     tag: 'div',
34415     
34416     cls: '',
34417     
34418     bricks: null, //CompositeElement
34419     
34420     cols : 1,
34421     
34422     _isLayoutInited : false,
34423     
34424 //    isAlternative : false, // only use for vertical layout...
34425     
34426     /**
34427      * @cfg {Number} alternativePadWidth padding below box..
34428      */   
34429     alternativePadWidth : 50,
34430     
34431     selectedBrick : [],
34432     
34433     getAutoCreate : function(){
34434         
34435         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34436         
34437         var cfg = {
34438             tag: this.tag,
34439             cls: 'blog-masonary-wrapper ' + this.cls,
34440             cn : {
34441                 cls : 'mas-boxes masonary'
34442             }
34443         };
34444         
34445         return cfg;
34446     },
34447     
34448     getChildContainer: function( )
34449     {
34450         if (this.boxesEl) {
34451             return this.boxesEl;
34452         }
34453         
34454         this.boxesEl = this.el.select('.mas-boxes').first();
34455         
34456         return this.boxesEl;
34457     },
34458     
34459     
34460     initEvents : function()
34461     {
34462         var _this = this;
34463         
34464         if(this.isAutoInitial){
34465             Roo.log('hook children rendered');
34466             this.on('childrenrendered', function() {
34467                 Roo.log('children rendered');
34468                 _this.initial();
34469             } ,this);
34470         }
34471     },
34472     
34473     initial : function()
34474     {
34475         this.selectedBrick = [];
34476         
34477         this.currentSize = this.el.getBox(true);
34478         
34479         Roo.EventManager.onWindowResize(this.resize, this); 
34480
34481         if(!this.isAutoInitial){
34482             this.layout();
34483             return;
34484         }
34485         
34486         this.layout();
34487         
34488         return;
34489         //this.layout.defer(500,this);
34490         
34491     },
34492     
34493     resize : function()
34494     {
34495         var cs = this.el.getBox(true);
34496         
34497         if (
34498                 this.currentSize.width == cs.width && 
34499                 this.currentSize.x == cs.x && 
34500                 this.currentSize.height == cs.height && 
34501                 this.currentSize.y == cs.y 
34502         ) {
34503             Roo.log("no change in with or X or Y");
34504             return;
34505         }
34506         
34507         this.currentSize = cs;
34508         
34509         this.layout();
34510         
34511     },
34512     
34513     layout : function()
34514     {   
34515         this._resetLayout();
34516         
34517         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34518         
34519         this.layoutItems( isInstant );
34520       
34521         this._isLayoutInited = true;
34522         
34523         this.fireEvent('layout', this);
34524         
34525     },
34526     
34527     _resetLayout : function()
34528     {
34529         if(this.isHorizontal){
34530             this.horizontalMeasureColumns();
34531             return;
34532         }
34533         
34534         this.verticalMeasureColumns();
34535         
34536     },
34537     
34538     verticalMeasureColumns : function()
34539     {
34540         this.getContainerWidth();
34541         
34542 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34543 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34544 //            return;
34545 //        }
34546         
34547         var boxWidth = this.boxWidth + this.padWidth;
34548         
34549         if(this.containerWidth < this.boxWidth){
34550             boxWidth = this.containerWidth
34551         }
34552         
34553         var containerWidth = this.containerWidth;
34554         
34555         var cols = Math.floor(containerWidth / boxWidth);
34556         
34557         this.cols = Math.max( cols, 1 );
34558         
34559         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34560         
34561         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34562         
34563         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34564         
34565         this.colWidth = boxWidth + avail - this.padWidth;
34566         
34567         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34568         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34569     },
34570     
34571     horizontalMeasureColumns : function()
34572     {
34573         this.getContainerWidth();
34574         
34575         var boxWidth = this.boxWidth;
34576         
34577         if(this.containerWidth < boxWidth){
34578             boxWidth = this.containerWidth;
34579         }
34580         
34581         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34582         
34583         this.el.setHeight(boxWidth);
34584         
34585     },
34586     
34587     getContainerWidth : function()
34588     {
34589         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34590     },
34591     
34592     layoutItems : function( isInstant )
34593     {
34594         Roo.log(this.bricks);
34595         
34596         var items = Roo.apply([], this.bricks);
34597         
34598         if(this.isHorizontal){
34599             this._horizontalLayoutItems( items , isInstant );
34600             return;
34601         }
34602         
34603 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34604 //            this._verticalAlternativeLayoutItems( items , isInstant );
34605 //            return;
34606 //        }
34607         
34608         this._verticalLayoutItems( items , isInstant );
34609         
34610     },
34611     
34612     _verticalLayoutItems : function ( items , isInstant)
34613     {
34614         if ( !items || !items.length ) {
34615             return;
34616         }
34617         
34618         var standard = [
34619             ['xs', 'xs', 'xs', 'tall'],
34620             ['xs', 'xs', 'tall'],
34621             ['xs', 'xs', 'sm'],
34622             ['xs', 'xs', 'xs'],
34623             ['xs', 'tall'],
34624             ['xs', 'sm'],
34625             ['xs', 'xs'],
34626             ['xs'],
34627             
34628             ['sm', 'xs', 'xs'],
34629             ['sm', 'xs'],
34630             ['sm'],
34631             
34632             ['tall', 'xs', 'xs', 'xs'],
34633             ['tall', 'xs', 'xs'],
34634             ['tall', 'xs'],
34635             ['tall']
34636             
34637         ];
34638         
34639         var queue = [];
34640         
34641         var boxes = [];
34642         
34643         var box = [];
34644         
34645         Roo.each(items, function(item, k){
34646             
34647             switch (item.size) {
34648                 // these layouts take up a full box,
34649                 case 'md' :
34650                 case 'md-left' :
34651                 case 'md-right' :
34652                 case 'wide' :
34653                     
34654                     if(box.length){
34655                         boxes.push(box);
34656                         box = [];
34657                     }
34658                     
34659                     boxes.push([item]);
34660                     
34661                     break;
34662                     
34663                 case 'xs' :
34664                 case 'sm' :
34665                 case 'tall' :
34666                     
34667                     box.push(item);
34668                     
34669                     break;
34670                 default :
34671                     break;
34672                     
34673             }
34674             
34675         }, this);
34676         
34677         if(box.length){
34678             boxes.push(box);
34679             box = [];
34680         }
34681         
34682         var filterPattern = function(box, length)
34683         {
34684             if(!box.length){
34685                 return;
34686             }
34687             
34688             var match = false;
34689             
34690             var pattern = box.slice(0, length);
34691             
34692             var format = [];
34693             
34694             Roo.each(pattern, function(i){
34695                 format.push(i.size);
34696             }, this);
34697             
34698             Roo.each(standard, function(s){
34699                 
34700                 if(String(s) != String(format)){
34701                     return;
34702                 }
34703                 
34704                 match = true;
34705                 return false;
34706                 
34707             }, this);
34708             
34709             if(!match && length == 1){
34710                 return;
34711             }
34712             
34713             if(!match){
34714                 filterPattern(box, length - 1);
34715                 return;
34716             }
34717                 
34718             queue.push(pattern);
34719
34720             box = box.slice(length, box.length);
34721
34722             filterPattern(box, 4);
34723
34724             return;
34725             
34726         }
34727         
34728         Roo.each(boxes, function(box, k){
34729             
34730             if(!box.length){
34731                 return;
34732             }
34733             
34734             if(box.length == 1){
34735                 queue.push(box);
34736                 return;
34737             }
34738             
34739             filterPattern(box, 4);
34740             
34741         }, this);
34742         
34743         this._processVerticalLayoutQueue( queue, isInstant );
34744         
34745     },
34746     
34747 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34748 //    {
34749 //        if ( !items || !items.length ) {
34750 //            return;
34751 //        }
34752 //
34753 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34754 //        
34755 //    },
34756     
34757     _horizontalLayoutItems : function ( items , isInstant)
34758     {
34759         if ( !items || !items.length || items.length < 3) {
34760             return;
34761         }
34762         
34763         items.reverse();
34764         
34765         var eItems = items.slice(0, 3);
34766         
34767         items = items.slice(3, items.length);
34768         
34769         var standard = [
34770             ['xs', 'xs', 'xs', 'wide'],
34771             ['xs', 'xs', 'wide'],
34772             ['xs', 'xs', 'sm'],
34773             ['xs', 'xs', 'xs'],
34774             ['xs', 'wide'],
34775             ['xs', 'sm'],
34776             ['xs', 'xs'],
34777             ['xs'],
34778             
34779             ['sm', 'xs', 'xs'],
34780             ['sm', 'xs'],
34781             ['sm'],
34782             
34783             ['wide', 'xs', 'xs', 'xs'],
34784             ['wide', 'xs', 'xs'],
34785             ['wide', 'xs'],
34786             ['wide'],
34787             
34788             ['wide-thin']
34789         ];
34790         
34791         var queue = [];
34792         
34793         var boxes = [];
34794         
34795         var box = [];
34796         
34797         Roo.each(items, function(item, k){
34798             
34799             switch (item.size) {
34800                 case 'md' :
34801                 case 'md-left' :
34802                 case 'md-right' :
34803                 case 'tall' :
34804                     
34805                     if(box.length){
34806                         boxes.push(box);
34807                         box = [];
34808                     }
34809                     
34810                     boxes.push([item]);
34811                     
34812                     break;
34813                     
34814                 case 'xs' :
34815                 case 'sm' :
34816                 case 'wide' :
34817                 case 'wide-thin' :
34818                     
34819                     box.push(item);
34820                     
34821                     break;
34822                 default :
34823                     break;
34824                     
34825             }
34826             
34827         }, this);
34828         
34829         if(box.length){
34830             boxes.push(box);
34831             box = [];
34832         }
34833         
34834         var filterPattern = function(box, length)
34835         {
34836             if(!box.length){
34837                 return;
34838             }
34839             
34840             var match = false;
34841             
34842             var pattern = box.slice(0, length);
34843             
34844             var format = [];
34845             
34846             Roo.each(pattern, function(i){
34847                 format.push(i.size);
34848             }, this);
34849             
34850             Roo.each(standard, function(s){
34851                 
34852                 if(String(s) != String(format)){
34853                     return;
34854                 }
34855                 
34856                 match = true;
34857                 return false;
34858                 
34859             }, this);
34860             
34861             if(!match && length == 1){
34862                 return;
34863             }
34864             
34865             if(!match){
34866                 filterPattern(box, length - 1);
34867                 return;
34868             }
34869                 
34870             queue.push(pattern);
34871
34872             box = box.slice(length, box.length);
34873
34874             filterPattern(box, 4);
34875
34876             return;
34877             
34878         }
34879         
34880         Roo.each(boxes, function(box, k){
34881             
34882             if(!box.length){
34883                 return;
34884             }
34885             
34886             if(box.length == 1){
34887                 queue.push(box);
34888                 return;
34889             }
34890             
34891             filterPattern(box, 4);
34892             
34893         }, this);
34894         
34895         
34896         var prune = [];
34897         
34898         var pos = this.el.getBox(true);
34899         
34900         var minX = pos.x;
34901         
34902         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34903         
34904         var hit_end = false;
34905         
34906         Roo.each(queue, function(box){
34907             
34908             if(hit_end){
34909                 
34910                 Roo.each(box, function(b){
34911                 
34912                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34913                     b.el.hide();
34914
34915                 }, this);
34916
34917                 return;
34918             }
34919             
34920             var mx = 0;
34921             
34922             Roo.each(box, function(b){
34923                 
34924                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34925                 b.el.show();
34926
34927                 mx = Math.max(mx, b.x);
34928                 
34929             }, this);
34930             
34931             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34932             
34933             if(maxX < minX){
34934                 
34935                 Roo.each(box, function(b){
34936                 
34937                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34938                     b.el.hide();
34939                     
34940                 }, this);
34941                 
34942                 hit_end = true;
34943                 
34944                 return;
34945             }
34946             
34947             prune.push(box);
34948             
34949         }, this);
34950         
34951         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34952     },
34953     
34954     /** Sets position of item in DOM
34955     * @param {Element} item
34956     * @param {Number} x - horizontal position
34957     * @param {Number} y - vertical position
34958     * @param {Boolean} isInstant - disables transitions
34959     */
34960     _processVerticalLayoutQueue : function( queue, isInstant )
34961     {
34962         var pos = this.el.getBox(true);
34963         var x = pos.x;
34964         var y = pos.y;
34965         var maxY = [];
34966         
34967         for (var i = 0; i < this.cols; i++){
34968             maxY[i] = pos.y;
34969         }
34970         
34971         Roo.each(queue, function(box, k){
34972             
34973             var col = k % this.cols;
34974             
34975             Roo.each(box, function(b,kk){
34976                 
34977                 b.el.position('absolute');
34978                 
34979                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34980                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34981                 
34982                 if(b.size == 'md-left' || b.size == 'md-right'){
34983                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34984                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34985                 }
34986                 
34987                 b.el.setWidth(width);
34988                 b.el.setHeight(height);
34989                 // iframe?
34990                 b.el.select('iframe',true).setSize(width,height);
34991                 
34992             }, this);
34993             
34994             for (var i = 0; i < this.cols; i++){
34995                 
34996                 if(maxY[i] < maxY[col]){
34997                     col = i;
34998                     continue;
34999                 }
35000                 
35001                 col = Math.min(col, i);
35002                 
35003             }
35004             
35005             x = pos.x + col * (this.colWidth + this.padWidth);
35006             
35007             y = maxY[col];
35008             
35009             var positions = [];
35010             
35011             switch (box.length){
35012                 case 1 :
35013                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35014                     break;
35015                 case 2 :
35016                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35017                     break;
35018                 case 3 :
35019                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35020                     break;
35021                 case 4 :
35022                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35023                     break;
35024                 default :
35025                     break;
35026             }
35027             
35028             Roo.each(box, function(b,kk){
35029                 
35030                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35031                 
35032                 var sz = b.el.getSize();
35033                 
35034                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35035                 
35036             }, this);
35037             
35038         }, this);
35039         
35040         var mY = 0;
35041         
35042         for (var i = 0; i < this.cols; i++){
35043             mY = Math.max(mY, maxY[i]);
35044         }
35045         
35046         this.el.setHeight(mY - pos.y);
35047         
35048     },
35049     
35050 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35051 //    {
35052 //        var pos = this.el.getBox(true);
35053 //        var x = pos.x;
35054 //        var y = pos.y;
35055 //        var maxX = pos.right;
35056 //        
35057 //        var maxHeight = 0;
35058 //        
35059 //        Roo.each(items, function(item, k){
35060 //            
35061 //            var c = k % 2;
35062 //            
35063 //            item.el.position('absolute');
35064 //                
35065 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35066 //
35067 //            item.el.setWidth(width);
35068 //
35069 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35070 //
35071 //            item.el.setHeight(height);
35072 //            
35073 //            if(c == 0){
35074 //                item.el.setXY([x, y], isInstant ? false : true);
35075 //            } else {
35076 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35077 //            }
35078 //            
35079 //            y = y + height + this.alternativePadWidth;
35080 //            
35081 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35082 //            
35083 //        }, this);
35084 //        
35085 //        this.el.setHeight(maxHeight);
35086 //        
35087 //    },
35088     
35089     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35090     {
35091         var pos = this.el.getBox(true);
35092         
35093         var minX = pos.x;
35094         var minY = pos.y;
35095         
35096         var maxX = pos.right;
35097         
35098         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35099         
35100         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35101         
35102         Roo.each(queue, function(box, k){
35103             
35104             Roo.each(box, function(b, kk){
35105                 
35106                 b.el.position('absolute');
35107                 
35108                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35109                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35110                 
35111                 if(b.size == 'md-left' || b.size == 'md-right'){
35112                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35113                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35114                 }
35115                 
35116                 b.el.setWidth(width);
35117                 b.el.setHeight(height);
35118                 
35119             }, this);
35120             
35121             if(!box.length){
35122                 return;
35123             }
35124             
35125             var positions = [];
35126             
35127             switch (box.length){
35128                 case 1 :
35129                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35130                     break;
35131                 case 2 :
35132                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35133                     break;
35134                 case 3 :
35135                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35136                     break;
35137                 case 4 :
35138                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35139                     break;
35140                 default :
35141                     break;
35142             }
35143             
35144             Roo.each(box, function(b,kk){
35145                 
35146                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35147                 
35148                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35149                 
35150             }, this);
35151             
35152         }, this);
35153         
35154     },
35155     
35156     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35157     {
35158         Roo.each(eItems, function(b,k){
35159             
35160             b.size = (k == 0) ? 'sm' : 'xs';
35161             b.x = (k == 0) ? 2 : 1;
35162             b.y = (k == 0) ? 2 : 1;
35163             
35164             b.el.position('absolute');
35165             
35166             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35167                 
35168             b.el.setWidth(width);
35169             
35170             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35171             
35172             b.el.setHeight(height);
35173             
35174         }, this);
35175
35176         var positions = [];
35177         
35178         positions.push({
35179             x : maxX - this.unitWidth * 2 - this.gutter,
35180             y : minY
35181         });
35182         
35183         positions.push({
35184             x : maxX - this.unitWidth,
35185             y : minY + (this.unitWidth + this.gutter) * 2
35186         });
35187         
35188         positions.push({
35189             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35190             y : minY
35191         });
35192         
35193         Roo.each(eItems, function(b,k){
35194             
35195             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35196
35197         }, this);
35198         
35199     },
35200     
35201     getVerticalOneBoxColPositions : function(x, y, box)
35202     {
35203         var pos = [];
35204         
35205         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35206         
35207         if(box[0].size == 'md-left'){
35208             rand = 0;
35209         }
35210         
35211         if(box[0].size == 'md-right'){
35212             rand = 1;
35213         }
35214         
35215         pos.push({
35216             x : x + (this.unitWidth + this.gutter) * rand,
35217             y : y
35218         });
35219         
35220         return pos;
35221     },
35222     
35223     getVerticalTwoBoxColPositions : function(x, y, box)
35224     {
35225         var pos = [];
35226         
35227         if(box[0].size == 'xs'){
35228             
35229             pos.push({
35230                 x : x,
35231                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35232             });
35233
35234             pos.push({
35235                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35236                 y : y
35237             });
35238             
35239             return pos;
35240             
35241         }
35242         
35243         pos.push({
35244             x : x,
35245             y : y
35246         });
35247
35248         pos.push({
35249             x : x + (this.unitWidth + this.gutter) * 2,
35250             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35251         });
35252         
35253         return pos;
35254         
35255     },
35256     
35257     getVerticalThreeBoxColPositions : function(x, y, box)
35258     {
35259         var pos = [];
35260         
35261         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35262             
35263             pos.push({
35264                 x : x,
35265                 y : y
35266             });
35267
35268             pos.push({
35269                 x : x + (this.unitWidth + this.gutter) * 1,
35270                 y : y
35271             });
35272             
35273             pos.push({
35274                 x : x + (this.unitWidth + this.gutter) * 2,
35275                 y : y
35276             });
35277             
35278             return pos;
35279             
35280         }
35281         
35282         if(box[0].size == 'xs' && box[1].size == 'xs'){
35283             
35284             pos.push({
35285                 x : x,
35286                 y : y
35287             });
35288
35289             pos.push({
35290                 x : x,
35291                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35292             });
35293             
35294             pos.push({
35295                 x : x + (this.unitWidth + this.gutter) * 1,
35296                 y : y
35297             });
35298             
35299             return pos;
35300             
35301         }
35302         
35303         pos.push({
35304             x : x,
35305             y : y
35306         });
35307
35308         pos.push({
35309             x : x + (this.unitWidth + this.gutter) * 2,
35310             y : y
35311         });
35312
35313         pos.push({
35314             x : x + (this.unitWidth + this.gutter) * 2,
35315             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35316         });
35317             
35318         return pos;
35319         
35320     },
35321     
35322     getVerticalFourBoxColPositions : function(x, y, box)
35323     {
35324         var pos = [];
35325         
35326         if(box[0].size == 'xs'){
35327             
35328             pos.push({
35329                 x : x,
35330                 y : y
35331             });
35332
35333             pos.push({
35334                 x : x,
35335                 y : y + (this.unitHeight + this.gutter) * 1
35336             });
35337             
35338             pos.push({
35339                 x : x,
35340                 y : y + (this.unitHeight + this.gutter) * 2
35341             });
35342             
35343             pos.push({
35344                 x : x + (this.unitWidth + this.gutter) * 1,
35345                 y : y
35346             });
35347             
35348             return pos;
35349             
35350         }
35351         
35352         pos.push({
35353             x : x,
35354             y : y
35355         });
35356
35357         pos.push({
35358             x : x + (this.unitWidth + this.gutter) * 2,
35359             y : y
35360         });
35361
35362         pos.push({
35363             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35364             y : y + (this.unitHeight + this.gutter) * 1
35365         });
35366
35367         pos.push({
35368             x : x + (this.unitWidth + this.gutter) * 2,
35369             y : y + (this.unitWidth + this.gutter) * 2
35370         });
35371
35372         return pos;
35373         
35374     },
35375     
35376     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35377     {
35378         var pos = [];
35379         
35380         if(box[0].size == 'md-left'){
35381             pos.push({
35382                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35383                 y : minY
35384             });
35385             
35386             return pos;
35387         }
35388         
35389         if(box[0].size == 'md-right'){
35390             pos.push({
35391                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35392                 y : minY + (this.unitWidth + this.gutter) * 1
35393             });
35394             
35395             return pos;
35396         }
35397         
35398         var rand = Math.floor(Math.random() * (4 - box[0].y));
35399         
35400         pos.push({
35401             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35402             y : minY + (this.unitWidth + this.gutter) * rand
35403         });
35404         
35405         return pos;
35406         
35407     },
35408     
35409     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35410     {
35411         var pos = [];
35412         
35413         if(box[0].size == 'xs'){
35414             
35415             pos.push({
35416                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35417                 y : minY
35418             });
35419
35420             pos.push({
35421                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35422                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35423             });
35424             
35425             return pos;
35426             
35427         }
35428         
35429         pos.push({
35430             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35431             y : minY
35432         });
35433
35434         pos.push({
35435             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35436             y : minY + (this.unitWidth + this.gutter) * 2
35437         });
35438         
35439         return pos;
35440         
35441     },
35442     
35443     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35444     {
35445         var pos = [];
35446         
35447         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35448             
35449             pos.push({
35450                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35451                 y : minY
35452             });
35453
35454             pos.push({
35455                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35456                 y : minY + (this.unitWidth + this.gutter) * 1
35457             });
35458             
35459             pos.push({
35460                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35461                 y : minY + (this.unitWidth + this.gutter) * 2
35462             });
35463             
35464             return pos;
35465             
35466         }
35467         
35468         if(box[0].size == 'xs' && box[1].size == 'xs'){
35469             
35470             pos.push({
35471                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35472                 y : minY
35473             });
35474
35475             pos.push({
35476                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35477                 y : minY
35478             });
35479             
35480             pos.push({
35481                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35482                 y : minY + (this.unitWidth + this.gutter) * 1
35483             });
35484             
35485             return pos;
35486             
35487         }
35488         
35489         pos.push({
35490             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35491             y : minY
35492         });
35493
35494         pos.push({
35495             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35496             y : minY + (this.unitWidth + this.gutter) * 2
35497         });
35498
35499         pos.push({
35500             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35501             y : minY + (this.unitWidth + this.gutter) * 2
35502         });
35503             
35504         return pos;
35505         
35506     },
35507     
35508     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35509     {
35510         var pos = [];
35511         
35512         if(box[0].size == 'xs'){
35513             
35514             pos.push({
35515                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35516                 y : minY
35517             });
35518
35519             pos.push({
35520                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35521                 y : minY
35522             });
35523             
35524             pos.push({
35525                 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),
35526                 y : minY
35527             });
35528             
35529             pos.push({
35530                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35531                 y : minY + (this.unitWidth + this.gutter) * 1
35532             });
35533             
35534             return pos;
35535             
35536         }
35537         
35538         pos.push({
35539             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35540             y : minY
35541         });
35542         
35543         pos.push({
35544             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35545             y : minY + (this.unitWidth + this.gutter) * 2
35546         });
35547         
35548         pos.push({
35549             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35550             y : minY + (this.unitWidth + this.gutter) * 2
35551         });
35552         
35553         pos.push({
35554             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),
35555             y : minY + (this.unitWidth + this.gutter) * 2
35556         });
35557
35558         return pos;
35559         
35560     },
35561     
35562     /**
35563     * remove a Masonry Brick
35564     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35565     */
35566     removeBrick : function(brick_id)
35567     {
35568         if (!brick_id) {
35569             return;
35570         }
35571         
35572         for (var i = 0; i<this.bricks.length; i++) {
35573             if (this.bricks[i].id == brick_id) {
35574                 this.bricks.splice(i,1);
35575                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35576                 this.initial();
35577             }
35578         }
35579     },
35580     
35581     /**
35582     * adds a Masonry Brick
35583     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35584     */
35585     addBrick : function(cfg)
35586     {
35587         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35588         //this.register(cn);
35589         cn.parentId = this.id;
35590         cn.render(this.el);
35591         return cn;
35592     },
35593     
35594     /**
35595     * register a Masonry Brick
35596     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35597     */
35598     
35599     register : function(brick)
35600     {
35601         this.bricks.push(brick);
35602         brick.masonryId = this.id;
35603     },
35604     
35605     /**
35606     * clear all the Masonry Brick
35607     */
35608     clearAll : function()
35609     {
35610         this.bricks = [];
35611         //this.getChildContainer().dom.innerHTML = "";
35612         this.el.dom.innerHTML = '';
35613     },
35614     
35615     getSelected : function()
35616     {
35617         if (!this.selectedBrick) {
35618             return false;
35619         }
35620         
35621         return this.selectedBrick;
35622     }
35623 });
35624
35625 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35626     
35627     groups: {},
35628      /**
35629     * register a Masonry Layout
35630     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35631     */
35632     
35633     register : function(layout)
35634     {
35635         this.groups[layout.id] = layout;
35636     },
35637     /**
35638     * fetch a  Masonry Layout based on the masonry layout ID
35639     * @param {string} the masonry layout to add
35640     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35641     */
35642     
35643     get: function(layout_id) {
35644         if (typeof(this.groups[layout_id]) == 'undefined') {
35645             return false;
35646         }
35647         return this.groups[layout_id] ;
35648     }
35649     
35650     
35651     
35652 });
35653
35654  
35655
35656  /**
35657  *
35658  * This is based on 
35659  * http://masonry.desandro.com
35660  *
35661  * The idea is to render all the bricks based on vertical width...
35662  *
35663  * The original code extends 'outlayer' - we might need to use that....
35664  * 
35665  */
35666
35667
35668 /**
35669  * @class Roo.bootstrap.LayoutMasonryAuto
35670  * @extends Roo.bootstrap.Component
35671  * Bootstrap Layout Masonry class
35672  * 
35673  * @constructor
35674  * Create a new Element
35675  * @param {Object} config The config object
35676  */
35677
35678 Roo.bootstrap.LayoutMasonryAuto = function(config){
35679     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35680 };
35681
35682 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35683     
35684       /**
35685      * @cfg {Boolean} isFitWidth  - resize the width..
35686      */   
35687     isFitWidth : false,  // options..
35688     /**
35689      * @cfg {Boolean} isOriginLeft = left align?
35690      */   
35691     isOriginLeft : true,
35692     /**
35693      * @cfg {Boolean} isOriginTop = top align?
35694      */   
35695     isOriginTop : false,
35696     /**
35697      * @cfg {Boolean} isLayoutInstant = no animation?
35698      */   
35699     isLayoutInstant : false, // needed?
35700     /**
35701      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35702      */   
35703     isResizingContainer : true,
35704     /**
35705      * @cfg {Number} columnWidth  width of the columns 
35706      */   
35707     
35708     columnWidth : 0,
35709     
35710     /**
35711      * @cfg {Number} maxCols maximum number of columns
35712      */   
35713     
35714     maxCols: 0,
35715     /**
35716      * @cfg {Number} padHeight padding below box..
35717      */   
35718     
35719     padHeight : 10, 
35720     
35721     /**
35722      * @cfg {Boolean} isAutoInitial defalut true
35723      */   
35724     
35725     isAutoInitial : true, 
35726     
35727     // private?
35728     gutter : 0,
35729     
35730     containerWidth: 0,
35731     initialColumnWidth : 0,
35732     currentSize : null,
35733     
35734     colYs : null, // array.
35735     maxY : 0,
35736     padWidth: 10,
35737     
35738     
35739     tag: 'div',
35740     cls: '',
35741     bricks: null, //CompositeElement
35742     cols : 0, // array?
35743     // element : null, // wrapped now this.el
35744     _isLayoutInited : null, 
35745     
35746     
35747     getAutoCreate : function(){
35748         
35749         var cfg = {
35750             tag: this.tag,
35751             cls: 'blog-masonary-wrapper ' + this.cls,
35752             cn : {
35753                 cls : 'mas-boxes masonary'
35754             }
35755         };
35756         
35757         return cfg;
35758     },
35759     
35760     getChildContainer: function( )
35761     {
35762         if (this.boxesEl) {
35763             return this.boxesEl;
35764         }
35765         
35766         this.boxesEl = this.el.select('.mas-boxes').first();
35767         
35768         return this.boxesEl;
35769     },
35770     
35771     
35772     initEvents : function()
35773     {
35774         var _this = this;
35775         
35776         if(this.isAutoInitial){
35777             Roo.log('hook children rendered');
35778             this.on('childrenrendered', function() {
35779                 Roo.log('children rendered');
35780                 _this.initial();
35781             } ,this);
35782         }
35783         
35784     },
35785     
35786     initial : function()
35787     {
35788         this.reloadItems();
35789
35790         this.currentSize = this.el.getBox(true);
35791
35792         /// was window resize... - let's see if this works..
35793         Roo.EventManager.onWindowResize(this.resize, this); 
35794
35795         if(!this.isAutoInitial){
35796             this.layout();
35797             return;
35798         }
35799         
35800         this.layout.defer(500,this);
35801     },
35802     
35803     reloadItems: function()
35804     {
35805         this.bricks = this.el.select('.masonry-brick', true);
35806         
35807         this.bricks.each(function(b) {
35808             //Roo.log(b.getSize());
35809             if (!b.attr('originalwidth')) {
35810                 b.attr('originalwidth',  b.getSize().width);
35811             }
35812             
35813         });
35814         
35815         Roo.log(this.bricks.elements.length);
35816     },
35817     
35818     resize : function()
35819     {
35820         Roo.log('resize');
35821         var cs = this.el.getBox(true);
35822         
35823         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35824             Roo.log("no change in with or X");
35825             return;
35826         }
35827         this.currentSize = cs;
35828         this.layout();
35829     },
35830     
35831     layout : function()
35832     {
35833          Roo.log('layout');
35834         this._resetLayout();
35835         //this._manageStamps();
35836       
35837         // don't animate first layout
35838         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35839         this.layoutItems( isInstant );
35840       
35841         // flag for initalized
35842         this._isLayoutInited = true;
35843     },
35844     
35845     layoutItems : function( isInstant )
35846     {
35847         //var items = this._getItemsForLayout( this.items );
35848         // original code supports filtering layout items.. we just ignore it..
35849         
35850         this._layoutItems( this.bricks , isInstant );
35851       
35852         this._postLayout();
35853     },
35854     _layoutItems : function ( items , isInstant)
35855     {
35856        //this.fireEvent( 'layout', this, items );
35857     
35858
35859         if ( !items || !items.elements.length ) {
35860           // no items, emit event with empty array
35861             return;
35862         }
35863
35864         var queue = [];
35865         items.each(function(item) {
35866             Roo.log("layout item");
35867             Roo.log(item);
35868             // get x/y object from method
35869             var position = this._getItemLayoutPosition( item );
35870             // enqueue
35871             position.item = item;
35872             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35873             queue.push( position );
35874         }, this);
35875       
35876         this._processLayoutQueue( queue );
35877     },
35878     /** Sets position of item in DOM
35879     * @param {Element} item
35880     * @param {Number} x - horizontal position
35881     * @param {Number} y - vertical position
35882     * @param {Boolean} isInstant - disables transitions
35883     */
35884     _processLayoutQueue : function( queue )
35885     {
35886         for ( var i=0, len = queue.length; i < len; i++ ) {
35887             var obj = queue[i];
35888             obj.item.position('absolute');
35889             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35890         }
35891     },
35892       
35893     
35894     /**
35895     * Any logic you want to do after each layout,
35896     * i.e. size the container
35897     */
35898     _postLayout : function()
35899     {
35900         this.resizeContainer();
35901     },
35902     
35903     resizeContainer : function()
35904     {
35905         if ( !this.isResizingContainer ) {
35906             return;
35907         }
35908         var size = this._getContainerSize();
35909         if ( size ) {
35910             this.el.setSize(size.width,size.height);
35911             this.boxesEl.setSize(size.width,size.height);
35912         }
35913     },
35914     
35915     
35916     
35917     _resetLayout : function()
35918     {
35919         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35920         this.colWidth = this.el.getWidth();
35921         //this.gutter = this.el.getWidth(); 
35922         
35923         this.measureColumns();
35924
35925         // reset column Y
35926         var i = this.cols;
35927         this.colYs = [];
35928         while (i--) {
35929             this.colYs.push( 0 );
35930         }
35931     
35932         this.maxY = 0;
35933     },
35934
35935     measureColumns : function()
35936     {
35937         this.getContainerWidth();
35938       // if columnWidth is 0, default to outerWidth of first item
35939         if ( !this.columnWidth ) {
35940             var firstItem = this.bricks.first();
35941             Roo.log(firstItem);
35942             this.columnWidth  = this.containerWidth;
35943             if (firstItem && firstItem.attr('originalwidth') ) {
35944                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35945             }
35946             // columnWidth fall back to item of first element
35947             Roo.log("set column width?");
35948                         this.initialColumnWidth = this.columnWidth  ;
35949
35950             // if first elem has no width, default to size of container
35951             
35952         }
35953         
35954         
35955         if (this.initialColumnWidth) {
35956             this.columnWidth = this.initialColumnWidth;
35957         }
35958         
35959         
35960             
35961         // column width is fixed at the top - however if container width get's smaller we should
35962         // reduce it...
35963         
35964         // this bit calcs how man columns..
35965             
35966         var columnWidth = this.columnWidth += this.gutter;
35967       
35968         // calculate columns
35969         var containerWidth = this.containerWidth + this.gutter;
35970         
35971         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35972         // fix rounding errors, typically with gutters
35973         var excess = columnWidth - containerWidth % columnWidth;
35974         
35975         
35976         // if overshoot is less than a pixel, round up, otherwise floor it
35977         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35978         cols = Math[ mathMethod ]( cols );
35979         this.cols = Math.max( cols, 1 );
35980         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35981         
35982          // padding positioning..
35983         var totalColWidth = this.cols * this.columnWidth;
35984         var padavail = this.containerWidth - totalColWidth;
35985         // so for 2 columns - we need 3 'pads'
35986         
35987         var padNeeded = (1+this.cols) * this.padWidth;
35988         
35989         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35990         
35991         this.columnWidth += padExtra
35992         //this.padWidth = Math.floor(padavail /  ( this.cols));
35993         
35994         // adjust colum width so that padding is fixed??
35995         
35996         // we have 3 columns ... total = width * 3
35997         // we have X left over... that should be used by 
35998         
35999         //if (this.expandC) {
36000             
36001         //}
36002         
36003         
36004         
36005     },
36006     
36007     getContainerWidth : function()
36008     {
36009        /* // container is parent if fit width
36010         var container = this.isFitWidth ? this.element.parentNode : this.element;
36011         // check that this.size and size are there
36012         // IE8 triggers resize on body size change, so they might not be
36013         
36014         var size = getSize( container );  //FIXME
36015         this.containerWidth = size && size.innerWidth; //FIXME
36016         */
36017          
36018         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36019         
36020     },
36021     
36022     _getItemLayoutPosition : function( item )  // what is item?
36023     {
36024         // we resize the item to our columnWidth..
36025       
36026         item.setWidth(this.columnWidth);
36027         item.autoBoxAdjust  = false;
36028         
36029         var sz = item.getSize();
36030  
36031         // how many columns does this brick span
36032         var remainder = this.containerWidth % this.columnWidth;
36033         
36034         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36035         // round if off by 1 pixel, otherwise use ceil
36036         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36037         colSpan = Math.min( colSpan, this.cols );
36038         
36039         // normally this should be '1' as we dont' currently allow multi width columns..
36040         
36041         var colGroup = this._getColGroup( colSpan );
36042         // get the minimum Y value from the columns
36043         var minimumY = Math.min.apply( Math, colGroup );
36044         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36045         
36046         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36047          
36048         // position the brick
36049         var position = {
36050             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36051             y: this.currentSize.y + minimumY + this.padHeight
36052         };
36053         
36054         Roo.log(position);
36055         // apply setHeight to necessary columns
36056         var setHeight = minimumY + sz.height + this.padHeight;
36057         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36058         
36059         var setSpan = this.cols + 1 - colGroup.length;
36060         for ( var i = 0; i < setSpan; i++ ) {
36061           this.colYs[ shortColIndex + i ] = setHeight ;
36062         }
36063       
36064         return position;
36065     },
36066     
36067     /**
36068      * @param {Number} colSpan - number of columns the element spans
36069      * @returns {Array} colGroup
36070      */
36071     _getColGroup : function( colSpan )
36072     {
36073         if ( colSpan < 2 ) {
36074           // if brick spans only one column, use all the column Ys
36075           return this.colYs;
36076         }
36077       
36078         var colGroup = [];
36079         // how many different places could this brick fit horizontally
36080         var groupCount = this.cols + 1 - colSpan;
36081         // for each group potential horizontal position
36082         for ( var i = 0; i < groupCount; i++ ) {
36083           // make an array of colY values for that one group
36084           var groupColYs = this.colYs.slice( i, i + colSpan );
36085           // and get the max value of the array
36086           colGroup[i] = Math.max.apply( Math, groupColYs );
36087         }
36088         return colGroup;
36089     },
36090     /*
36091     _manageStamp : function( stamp )
36092     {
36093         var stampSize =  stamp.getSize();
36094         var offset = stamp.getBox();
36095         // get the columns that this stamp affects
36096         var firstX = this.isOriginLeft ? offset.x : offset.right;
36097         var lastX = firstX + stampSize.width;
36098         var firstCol = Math.floor( firstX / this.columnWidth );
36099         firstCol = Math.max( 0, firstCol );
36100         
36101         var lastCol = Math.floor( lastX / this.columnWidth );
36102         // lastCol should not go over if multiple of columnWidth #425
36103         lastCol -= lastX % this.columnWidth ? 0 : 1;
36104         lastCol = Math.min( this.cols - 1, lastCol );
36105         
36106         // set colYs to bottom of the stamp
36107         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36108             stampSize.height;
36109             
36110         for ( var i = firstCol; i <= lastCol; i++ ) {
36111           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36112         }
36113     },
36114     */
36115     
36116     _getContainerSize : function()
36117     {
36118         this.maxY = Math.max.apply( Math, this.colYs );
36119         var size = {
36120             height: this.maxY
36121         };
36122       
36123         if ( this.isFitWidth ) {
36124             size.width = this._getContainerFitWidth();
36125         }
36126       
36127         return size;
36128     },
36129     
36130     _getContainerFitWidth : function()
36131     {
36132         var unusedCols = 0;
36133         // count unused columns
36134         var i = this.cols;
36135         while ( --i ) {
36136           if ( this.colYs[i] !== 0 ) {
36137             break;
36138           }
36139           unusedCols++;
36140         }
36141         // fit container to columns that have been used
36142         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36143     },
36144     
36145     needsResizeLayout : function()
36146     {
36147         var previousWidth = this.containerWidth;
36148         this.getContainerWidth();
36149         return previousWidth !== this.containerWidth;
36150     }
36151  
36152 });
36153
36154  
36155
36156  /*
36157  * - LGPL
36158  *
36159  * element
36160  * 
36161  */
36162
36163 /**
36164  * @class Roo.bootstrap.MasonryBrick
36165  * @extends Roo.bootstrap.Component
36166  * Bootstrap MasonryBrick class
36167  * 
36168  * @constructor
36169  * Create a new MasonryBrick
36170  * @param {Object} config The config object
36171  */
36172
36173 Roo.bootstrap.MasonryBrick = function(config){
36174     
36175     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36176     
36177     Roo.bootstrap.MasonryBrick.register(this);
36178     
36179     this.addEvents({
36180         // raw events
36181         /**
36182          * @event click
36183          * When a MasonryBrick is clcik
36184          * @param {Roo.bootstrap.MasonryBrick} this
36185          * @param {Roo.EventObject} e
36186          */
36187         "click" : true
36188     });
36189 };
36190
36191 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36192     
36193     /**
36194      * @cfg {String} title
36195      */   
36196     title : '',
36197     /**
36198      * @cfg {String} html
36199      */   
36200     html : '',
36201     /**
36202      * @cfg {String} bgimage
36203      */   
36204     bgimage : '',
36205     /**
36206      * @cfg {String} videourl
36207      */   
36208     videourl : '',
36209     /**
36210      * @cfg {String} cls
36211      */   
36212     cls : '',
36213     /**
36214      * @cfg {String} href
36215      */   
36216     href : '',
36217     /**
36218      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36219      */   
36220     size : 'xs',
36221     
36222     /**
36223      * @cfg {String} placetitle (center|bottom)
36224      */   
36225     placetitle : '',
36226     
36227     /**
36228      * @cfg {Boolean} isFitContainer defalut true
36229      */   
36230     isFitContainer : true, 
36231     
36232     /**
36233      * @cfg {Boolean} preventDefault defalut false
36234      */   
36235     preventDefault : false, 
36236     
36237     /**
36238      * @cfg {Boolean} inverse defalut false
36239      */   
36240     maskInverse : false, 
36241     
36242     getAutoCreate : function()
36243     {
36244         if(!this.isFitContainer){
36245             return this.getSplitAutoCreate();
36246         }
36247         
36248         var cls = 'masonry-brick masonry-brick-full';
36249         
36250         if(this.href.length){
36251             cls += ' masonry-brick-link';
36252         }
36253         
36254         if(this.bgimage.length){
36255             cls += ' masonry-brick-image';
36256         }
36257         
36258         if(this.maskInverse){
36259             cls += ' mask-inverse';
36260         }
36261         
36262         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36263             cls += ' enable-mask';
36264         }
36265         
36266         if(this.size){
36267             cls += ' masonry-' + this.size + '-brick';
36268         }
36269         
36270         if(this.placetitle.length){
36271             
36272             switch (this.placetitle) {
36273                 case 'center' :
36274                     cls += ' masonry-center-title';
36275                     break;
36276                 case 'bottom' :
36277                     cls += ' masonry-bottom-title';
36278                     break;
36279                 default:
36280                     break;
36281             }
36282             
36283         } else {
36284             if(!this.html.length && !this.bgimage.length){
36285                 cls += ' masonry-center-title';
36286             }
36287
36288             if(!this.html.length && this.bgimage.length){
36289                 cls += ' masonry-bottom-title';
36290             }
36291         }
36292         
36293         if(this.cls){
36294             cls += ' ' + this.cls;
36295         }
36296         
36297         var cfg = {
36298             tag: (this.href.length) ? 'a' : 'div',
36299             cls: cls,
36300             cn: [
36301                 {
36302                     tag: 'div',
36303                     cls: 'masonry-brick-mask'
36304                 },
36305                 {
36306                     tag: 'div',
36307                     cls: 'masonry-brick-paragraph',
36308                     cn: []
36309                 }
36310             ]
36311         };
36312         
36313         if(this.href.length){
36314             cfg.href = this.href;
36315         }
36316         
36317         var cn = cfg.cn[1].cn;
36318         
36319         if(this.title.length){
36320             cn.push({
36321                 tag: 'h4',
36322                 cls: 'masonry-brick-title',
36323                 html: this.title
36324             });
36325         }
36326         
36327         if(this.html.length){
36328             cn.push({
36329                 tag: 'p',
36330                 cls: 'masonry-brick-text',
36331                 html: this.html
36332             });
36333         }
36334         
36335         if (!this.title.length && !this.html.length) {
36336             cfg.cn[1].cls += ' hide';
36337         }
36338         
36339         if(this.bgimage.length){
36340             cfg.cn.push({
36341                 tag: 'img',
36342                 cls: 'masonry-brick-image-view',
36343                 src: this.bgimage
36344             });
36345         }
36346         
36347         if(this.videourl.length){
36348             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36349             // youtube support only?
36350             cfg.cn.push({
36351                 tag: 'iframe',
36352                 cls: 'masonry-brick-image-view',
36353                 src: vurl,
36354                 frameborder : 0,
36355                 allowfullscreen : true
36356             });
36357         }
36358         
36359         return cfg;
36360         
36361     },
36362     
36363     getSplitAutoCreate : function()
36364     {
36365         var cls = 'masonry-brick masonry-brick-split';
36366         
36367         if(this.href.length){
36368             cls += ' masonry-brick-link';
36369         }
36370         
36371         if(this.bgimage.length){
36372             cls += ' masonry-brick-image';
36373         }
36374         
36375         if(this.size){
36376             cls += ' masonry-' + this.size + '-brick';
36377         }
36378         
36379         switch (this.placetitle) {
36380             case 'center' :
36381                 cls += ' masonry-center-title';
36382                 break;
36383             case 'bottom' :
36384                 cls += ' masonry-bottom-title';
36385                 break;
36386             default:
36387                 if(!this.bgimage.length){
36388                     cls += ' masonry-center-title';
36389                 }
36390
36391                 if(this.bgimage.length){
36392                     cls += ' masonry-bottom-title';
36393                 }
36394                 break;
36395         }
36396         
36397         if(this.cls){
36398             cls += ' ' + this.cls;
36399         }
36400         
36401         var cfg = {
36402             tag: (this.href.length) ? 'a' : 'div',
36403             cls: cls,
36404             cn: [
36405                 {
36406                     tag: 'div',
36407                     cls: 'masonry-brick-split-head',
36408                     cn: [
36409                         {
36410                             tag: 'div',
36411                             cls: 'masonry-brick-paragraph',
36412                             cn: []
36413                         }
36414                     ]
36415                 },
36416                 {
36417                     tag: 'div',
36418                     cls: 'masonry-brick-split-body',
36419                     cn: []
36420                 }
36421             ]
36422         };
36423         
36424         if(this.href.length){
36425             cfg.href = this.href;
36426         }
36427         
36428         if(this.title.length){
36429             cfg.cn[0].cn[0].cn.push({
36430                 tag: 'h4',
36431                 cls: 'masonry-brick-title',
36432                 html: this.title
36433             });
36434         }
36435         
36436         if(this.html.length){
36437             cfg.cn[1].cn.push({
36438                 tag: 'p',
36439                 cls: 'masonry-brick-text',
36440                 html: this.html
36441             });
36442         }
36443
36444         if(this.bgimage.length){
36445             cfg.cn[0].cn.push({
36446                 tag: 'img',
36447                 cls: 'masonry-brick-image-view',
36448                 src: this.bgimage
36449             });
36450         }
36451         
36452         if(this.videourl.length){
36453             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36454             // youtube support only?
36455             cfg.cn[0].cn.cn.push({
36456                 tag: 'iframe',
36457                 cls: 'masonry-brick-image-view',
36458                 src: vurl,
36459                 frameborder : 0,
36460                 allowfullscreen : true
36461             });
36462         }
36463         
36464         return cfg;
36465     },
36466     
36467     initEvents: function() 
36468     {
36469         switch (this.size) {
36470             case 'xs' :
36471                 this.x = 1;
36472                 this.y = 1;
36473                 break;
36474             case 'sm' :
36475                 this.x = 2;
36476                 this.y = 2;
36477                 break;
36478             case 'md' :
36479             case 'md-left' :
36480             case 'md-right' :
36481                 this.x = 3;
36482                 this.y = 3;
36483                 break;
36484             case 'tall' :
36485                 this.x = 2;
36486                 this.y = 3;
36487                 break;
36488             case 'wide' :
36489                 this.x = 3;
36490                 this.y = 2;
36491                 break;
36492             case 'wide-thin' :
36493                 this.x = 3;
36494                 this.y = 1;
36495                 break;
36496                         
36497             default :
36498                 break;
36499         }
36500         
36501         if(Roo.isTouch){
36502             this.el.on('touchstart', this.onTouchStart, this);
36503             this.el.on('touchmove', this.onTouchMove, this);
36504             this.el.on('touchend', this.onTouchEnd, this);
36505             this.el.on('contextmenu', this.onContextMenu, this);
36506         } else {
36507             this.el.on('mouseenter'  ,this.enter, this);
36508             this.el.on('mouseleave', this.leave, this);
36509             this.el.on('click', this.onClick, this);
36510         }
36511         
36512         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36513             this.parent().bricks.push(this);   
36514         }
36515         
36516     },
36517     
36518     onClick: function(e, el)
36519     {
36520         var time = this.endTimer - this.startTimer;
36521         // Roo.log(e.preventDefault());
36522         if(Roo.isTouch){
36523             if(time > 1000){
36524                 e.preventDefault();
36525                 return;
36526             }
36527         }
36528         
36529         if(!this.preventDefault){
36530             return;
36531         }
36532         
36533         e.preventDefault();
36534         
36535         if (this.activeClass != '') {
36536             this.selectBrick();
36537         }
36538         
36539         this.fireEvent('click', this, e);
36540     },
36541     
36542     enter: function(e, el)
36543     {
36544         e.preventDefault();
36545         
36546         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36547             return;
36548         }
36549         
36550         if(this.bgimage.length && this.html.length){
36551             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36552         }
36553     },
36554     
36555     leave: function(e, el)
36556     {
36557         e.preventDefault();
36558         
36559         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36560             return;
36561         }
36562         
36563         if(this.bgimage.length && this.html.length){
36564             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36565         }
36566     },
36567     
36568     onTouchStart: function(e, el)
36569     {
36570 //        e.preventDefault();
36571         
36572         this.touchmoved = false;
36573         
36574         if(!this.isFitContainer){
36575             return;
36576         }
36577         
36578         if(!this.bgimage.length || !this.html.length){
36579             return;
36580         }
36581         
36582         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36583         
36584         this.timer = new Date().getTime();
36585         
36586     },
36587     
36588     onTouchMove: function(e, el)
36589     {
36590         this.touchmoved = true;
36591     },
36592     
36593     onContextMenu : function(e,el)
36594     {
36595         e.preventDefault();
36596         e.stopPropagation();
36597         return false;
36598     },
36599     
36600     onTouchEnd: function(e, el)
36601     {
36602 //        e.preventDefault();
36603         
36604         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36605         
36606             this.leave(e,el);
36607             
36608             return;
36609         }
36610         
36611         if(!this.bgimage.length || !this.html.length){
36612             
36613             if(this.href.length){
36614                 window.location.href = this.href;
36615             }
36616             
36617             return;
36618         }
36619         
36620         if(!this.isFitContainer){
36621             return;
36622         }
36623         
36624         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36625         
36626         window.location.href = this.href;
36627     },
36628     
36629     //selection on single brick only
36630     selectBrick : function() {
36631         
36632         if (!this.parentId) {
36633             return;
36634         }
36635         
36636         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36637         var index = m.selectedBrick.indexOf(this.id);
36638         
36639         if ( index > -1) {
36640             m.selectedBrick.splice(index,1);
36641             this.el.removeClass(this.activeClass);
36642             return;
36643         }
36644         
36645         for(var i = 0; i < m.selectedBrick.length; i++) {
36646             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36647             b.el.removeClass(b.activeClass);
36648         }
36649         
36650         m.selectedBrick = [];
36651         
36652         m.selectedBrick.push(this.id);
36653         this.el.addClass(this.activeClass);
36654         return;
36655     },
36656     
36657     isSelected : function(){
36658         return this.el.hasClass(this.activeClass);
36659         
36660     }
36661 });
36662
36663 Roo.apply(Roo.bootstrap.MasonryBrick, {
36664     
36665     //groups: {},
36666     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36667      /**
36668     * register a Masonry Brick
36669     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36670     */
36671     
36672     register : function(brick)
36673     {
36674         //this.groups[brick.id] = brick;
36675         this.groups.add(brick.id, brick);
36676     },
36677     /**
36678     * fetch a  masonry brick based on the masonry brick ID
36679     * @param {string} the masonry brick to add
36680     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36681     */
36682     
36683     get: function(brick_id) 
36684     {
36685         // if (typeof(this.groups[brick_id]) == 'undefined') {
36686         //     return false;
36687         // }
36688         // return this.groups[brick_id] ;
36689         
36690         if(this.groups.key(brick_id)) {
36691             return this.groups.key(brick_id);
36692         }
36693         
36694         return false;
36695     }
36696     
36697     
36698     
36699 });
36700
36701  /*
36702  * - LGPL
36703  *
36704  * element
36705  * 
36706  */
36707
36708 /**
36709  * @class Roo.bootstrap.Brick
36710  * @extends Roo.bootstrap.Component
36711  * Bootstrap Brick class
36712  * 
36713  * @constructor
36714  * Create a new Brick
36715  * @param {Object} config The config object
36716  */
36717
36718 Roo.bootstrap.Brick = function(config){
36719     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36720     
36721     this.addEvents({
36722         // raw events
36723         /**
36724          * @event click
36725          * When a Brick is click
36726          * @param {Roo.bootstrap.Brick} this
36727          * @param {Roo.EventObject} e
36728          */
36729         "click" : true
36730     });
36731 };
36732
36733 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36734     
36735     /**
36736      * @cfg {String} title
36737      */   
36738     title : '',
36739     /**
36740      * @cfg {String} html
36741      */   
36742     html : '',
36743     /**
36744      * @cfg {String} bgimage
36745      */   
36746     bgimage : '',
36747     /**
36748      * @cfg {String} cls
36749      */   
36750     cls : '',
36751     /**
36752      * @cfg {String} href
36753      */   
36754     href : '',
36755     /**
36756      * @cfg {String} video
36757      */   
36758     video : '',
36759     /**
36760      * @cfg {Boolean} square
36761      */   
36762     square : true,
36763     
36764     getAutoCreate : function()
36765     {
36766         var cls = 'roo-brick';
36767         
36768         if(this.href.length){
36769             cls += ' roo-brick-link';
36770         }
36771         
36772         if(this.bgimage.length){
36773             cls += ' roo-brick-image';
36774         }
36775         
36776         if(!this.html.length && !this.bgimage.length){
36777             cls += ' roo-brick-center-title';
36778         }
36779         
36780         if(!this.html.length && this.bgimage.length){
36781             cls += ' roo-brick-bottom-title';
36782         }
36783         
36784         if(this.cls){
36785             cls += ' ' + this.cls;
36786         }
36787         
36788         var cfg = {
36789             tag: (this.href.length) ? 'a' : 'div',
36790             cls: cls,
36791             cn: [
36792                 {
36793                     tag: 'div',
36794                     cls: 'roo-brick-paragraph',
36795                     cn: []
36796                 }
36797             ]
36798         };
36799         
36800         if(this.href.length){
36801             cfg.href = this.href;
36802         }
36803         
36804         var cn = cfg.cn[0].cn;
36805         
36806         if(this.title.length){
36807             cn.push({
36808                 tag: 'h4',
36809                 cls: 'roo-brick-title',
36810                 html: this.title
36811             });
36812         }
36813         
36814         if(this.html.length){
36815             cn.push({
36816                 tag: 'p',
36817                 cls: 'roo-brick-text',
36818                 html: this.html
36819             });
36820         } else {
36821             cn.cls += ' hide';
36822         }
36823         
36824         if(this.bgimage.length){
36825             cfg.cn.push({
36826                 tag: 'img',
36827                 cls: 'roo-brick-image-view',
36828                 src: this.bgimage
36829             });
36830         }
36831         
36832         return cfg;
36833     },
36834     
36835     initEvents: function() 
36836     {
36837         if(this.title.length || this.html.length){
36838             this.el.on('mouseenter'  ,this.enter, this);
36839             this.el.on('mouseleave', this.leave, this);
36840         }
36841         
36842         Roo.EventManager.onWindowResize(this.resize, this); 
36843         
36844         if(this.bgimage.length){
36845             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36846             this.imageEl.on('load', this.onImageLoad, this);
36847             return;
36848         }
36849         
36850         this.resize();
36851     },
36852     
36853     onImageLoad : function()
36854     {
36855         this.resize();
36856     },
36857     
36858     resize : function()
36859     {
36860         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36861         
36862         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36863         
36864         if(this.bgimage.length){
36865             var image = this.el.select('.roo-brick-image-view', true).first();
36866             
36867             image.setWidth(paragraph.getWidth());
36868             
36869             if(this.square){
36870                 image.setHeight(paragraph.getWidth());
36871             }
36872             
36873             this.el.setHeight(image.getHeight());
36874             paragraph.setHeight(image.getHeight());
36875             
36876         }
36877         
36878     },
36879     
36880     enter: function(e, el)
36881     {
36882         e.preventDefault();
36883         
36884         if(this.bgimage.length){
36885             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36886             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36887         }
36888     },
36889     
36890     leave: function(e, el)
36891     {
36892         e.preventDefault();
36893         
36894         if(this.bgimage.length){
36895             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36896             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36897         }
36898     }
36899     
36900 });
36901
36902  
36903
36904  /*
36905  * - LGPL
36906  *
36907  * Number field 
36908  */
36909
36910 /**
36911  * @class Roo.bootstrap.NumberField
36912  * @extends Roo.bootstrap.Input
36913  * Bootstrap NumberField class
36914  * 
36915  * 
36916  * 
36917  * 
36918  * @constructor
36919  * Create a new NumberField
36920  * @param {Object} config The config object
36921  */
36922
36923 Roo.bootstrap.NumberField = function(config){
36924     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36925 };
36926
36927 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36928     
36929     /**
36930      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36931      */
36932     allowDecimals : true,
36933     /**
36934      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36935      */
36936     decimalSeparator : ".",
36937     /**
36938      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36939      */
36940     decimalPrecision : 2,
36941     /**
36942      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36943      */
36944     allowNegative : true,
36945     
36946     /**
36947      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36948      */
36949     allowZero: true,
36950     /**
36951      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36952      */
36953     minValue : Number.NEGATIVE_INFINITY,
36954     /**
36955      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36956      */
36957     maxValue : Number.MAX_VALUE,
36958     /**
36959      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36960      */
36961     minText : "The minimum value for this field is {0}",
36962     /**
36963      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36964      */
36965     maxText : "The maximum value for this field is {0}",
36966     /**
36967      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36968      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36969      */
36970     nanText : "{0} is not a valid number",
36971     /**
36972      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36973      */
36974     thousandsDelimiter : false,
36975     /**
36976      * @cfg {String} valueAlign alignment of value
36977      */
36978     valueAlign : "left",
36979
36980     getAutoCreate : function()
36981     {
36982         var hiddenInput = {
36983             tag: 'input',
36984             type: 'hidden',
36985             id: Roo.id(),
36986             cls: 'hidden-number-input'
36987         };
36988         
36989         if (this.name) {
36990             hiddenInput.name = this.name;
36991         }
36992         
36993         this.name = '';
36994         
36995         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36996         
36997         this.name = hiddenInput.name;
36998         
36999         if(cfg.cn.length > 0) {
37000             cfg.cn.push(hiddenInput);
37001         }
37002         
37003         return cfg;
37004     },
37005
37006     // private
37007     initEvents : function()
37008     {   
37009         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37010         
37011         var allowed = "0123456789";
37012         
37013         if(this.allowDecimals){
37014             allowed += this.decimalSeparator;
37015         }
37016         
37017         if(this.allowNegative){
37018             allowed += "-";
37019         }
37020         
37021         if(this.thousandsDelimiter) {
37022             allowed += ",";
37023         }
37024         
37025         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37026         
37027         var keyPress = function(e){
37028             
37029             var k = e.getKey();
37030             
37031             var c = e.getCharCode();
37032             
37033             if(
37034                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37035                     allowed.indexOf(String.fromCharCode(c)) === -1
37036             ){
37037                 e.stopEvent();
37038                 return;
37039             }
37040             
37041             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37042                 return;
37043             }
37044             
37045             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37046                 e.stopEvent();
37047             }
37048         };
37049         
37050         this.el.on("keypress", keyPress, this);
37051     },
37052     
37053     validateValue : function(value)
37054     {
37055         
37056         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37057             return false;
37058         }
37059         
37060         var num = this.parseValue(value);
37061         
37062         if(isNaN(num)){
37063             this.markInvalid(String.format(this.nanText, value));
37064             return false;
37065         }
37066         
37067         if(num < this.minValue){
37068             this.markInvalid(String.format(this.minText, this.minValue));
37069             return false;
37070         }
37071         
37072         if(num > this.maxValue){
37073             this.markInvalid(String.format(this.maxText, this.maxValue));
37074             return false;
37075         }
37076         
37077         return true;
37078     },
37079
37080     getValue : function()
37081     {
37082         var v = this.hiddenEl().getValue();
37083         
37084         return this.fixPrecision(this.parseValue(v));
37085     },
37086
37087     parseValue : function(value)
37088     {
37089         if(this.thousandsDelimiter) {
37090             value += "";
37091             r = new RegExp(",", "g");
37092             value = value.replace(r, "");
37093         }
37094         
37095         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37096         return isNaN(value) ? '' : value;
37097     },
37098
37099     fixPrecision : function(value)
37100     {
37101         if(this.thousandsDelimiter) {
37102             value += "";
37103             r = new RegExp(",", "g");
37104             value = value.replace(r, "");
37105         }
37106         
37107         var nan = isNaN(value);
37108         
37109         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37110             return nan ? '' : value;
37111         }
37112         return parseFloat(value).toFixed(this.decimalPrecision);
37113     },
37114
37115     setValue : function(v)
37116     {
37117         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37118         
37119         this.value = v;
37120         
37121         if(this.rendered){
37122             
37123             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37124             
37125             this.inputEl().dom.value = (v == '') ? '' :
37126                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37127             
37128             if(!this.allowZero && v === '0') {
37129                 this.hiddenEl().dom.value = '';
37130                 this.inputEl().dom.value = '';
37131             }
37132             
37133             this.validate();
37134         }
37135     },
37136
37137     decimalPrecisionFcn : function(v)
37138     {
37139         return Math.floor(v);
37140     },
37141
37142     beforeBlur : function()
37143     {
37144         var v = this.parseValue(this.getRawValue());
37145         
37146         if(v || v === 0 || v === ''){
37147             this.setValue(v);
37148         }
37149     },
37150     
37151     hiddenEl : function()
37152     {
37153         return this.el.select('input.hidden-number-input',true).first();
37154     }
37155     
37156 });
37157
37158  
37159
37160 /*
37161 * Licence: LGPL
37162 */
37163
37164 /**
37165  * @class Roo.bootstrap.DocumentSlider
37166  * @extends Roo.bootstrap.Component
37167  * Bootstrap DocumentSlider class
37168  * 
37169  * @constructor
37170  * Create a new DocumentViewer
37171  * @param {Object} config The config object
37172  */
37173
37174 Roo.bootstrap.DocumentSlider = function(config){
37175     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37176     
37177     this.files = [];
37178     
37179     this.addEvents({
37180         /**
37181          * @event initial
37182          * Fire after initEvent
37183          * @param {Roo.bootstrap.DocumentSlider} this
37184          */
37185         "initial" : true,
37186         /**
37187          * @event update
37188          * Fire after update
37189          * @param {Roo.bootstrap.DocumentSlider} this
37190          */
37191         "update" : true,
37192         /**
37193          * @event click
37194          * Fire after click
37195          * @param {Roo.bootstrap.DocumentSlider} this
37196          */
37197         "click" : true
37198     });
37199 };
37200
37201 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37202     
37203     files : false,
37204     
37205     indicator : 0,
37206     
37207     getAutoCreate : function()
37208     {
37209         var cfg = {
37210             tag : 'div',
37211             cls : 'roo-document-slider',
37212             cn : [
37213                 {
37214                     tag : 'div',
37215                     cls : 'roo-document-slider-header',
37216                     cn : [
37217                         {
37218                             tag : 'div',
37219                             cls : 'roo-document-slider-header-title'
37220                         }
37221                     ]
37222                 },
37223                 {
37224                     tag : 'div',
37225                     cls : 'roo-document-slider-body',
37226                     cn : [
37227                         {
37228                             tag : 'div',
37229                             cls : 'roo-document-slider-prev',
37230                             cn : [
37231                                 {
37232                                     tag : 'i',
37233                                     cls : 'fa fa-chevron-left'
37234                                 }
37235                             ]
37236                         },
37237                         {
37238                             tag : 'div',
37239                             cls : 'roo-document-slider-thumb',
37240                             cn : [
37241                                 {
37242                                     tag : 'img',
37243                                     cls : 'roo-document-slider-image'
37244                                 }
37245                             ]
37246                         },
37247                         {
37248                             tag : 'div',
37249                             cls : 'roo-document-slider-next',
37250                             cn : [
37251                                 {
37252                                     tag : 'i',
37253                                     cls : 'fa fa-chevron-right'
37254                                 }
37255                             ]
37256                         }
37257                     ]
37258                 }
37259             ]
37260         };
37261         
37262         return cfg;
37263     },
37264     
37265     initEvents : function()
37266     {
37267         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37268         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37269         
37270         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37271         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37272         
37273         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37274         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37275         
37276         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37277         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37278         
37279         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37280         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37281         
37282         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37283         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37284         
37285         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37286         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37287         
37288         this.thumbEl.on('click', this.onClick, this);
37289         
37290         this.prevIndicator.on('click', this.prev, this);
37291         
37292         this.nextIndicator.on('click', this.next, this);
37293         
37294     },
37295     
37296     initial : function()
37297     {
37298         if(this.files.length){
37299             this.indicator = 1;
37300             this.update()
37301         }
37302         
37303         this.fireEvent('initial', this);
37304     },
37305     
37306     update : function()
37307     {
37308         this.imageEl.attr('src', this.files[this.indicator - 1]);
37309         
37310         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37311         
37312         this.prevIndicator.show();
37313         
37314         if(this.indicator == 1){
37315             this.prevIndicator.hide();
37316         }
37317         
37318         this.nextIndicator.show();
37319         
37320         if(this.indicator == this.files.length){
37321             this.nextIndicator.hide();
37322         }
37323         
37324         this.thumbEl.scrollTo('top');
37325         
37326         this.fireEvent('update', this);
37327     },
37328     
37329     onClick : function(e)
37330     {
37331         e.preventDefault();
37332         
37333         this.fireEvent('click', this);
37334     },
37335     
37336     prev : function(e)
37337     {
37338         e.preventDefault();
37339         
37340         this.indicator = Math.max(1, this.indicator - 1);
37341         
37342         this.update();
37343     },
37344     
37345     next : function(e)
37346     {
37347         e.preventDefault();
37348         
37349         this.indicator = Math.min(this.files.length, this.indicator + 1);
37350         
37351         this.update();
37352     }
37353 });
37354 /*
37355  * - LGPL
37356  *
37357  * RadioSet
37358  *
37359  *
37360  */
37361
37362 /**
37363  * @class Roo.bootstrap.RadioSet
37364  * @extends Roo.bootstrap.Input
37365  * Bootstrap RadioSet class
37366  * @cfg {String} indicatorpos (left|right) default left
37367  * @cfg {Boolean} inline (true|false) inline the element (default true)
37368  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37369  * @constructor
37370  * Create a new RadioSet
37371  * @param {Object} config The config object
37372  */
37373
37374 Roo.bootstrap.RadioSet = function(config){
37375     
37376     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37377     
37378     this.radioes = [];
37379     
37380     Roo.bootstrap.RadioSet.register(this);
37381     
37382     this.addEvents({
37383         /**
37384         * @event check
37385         * Fires when the element is checked or unchecked.
37386         * @param {Roo.bootstrap.RadioSet} this This radio
37387         * @param {Roo.bootstrap.Radio} item The checked item
37388         */
37389        check : true,
37390        /**
37391         * @event click
37392         * Fires when the element is click.
37393         * @param {Roo.bootstrap.RadioSet} this This radio set
37394         * @param {Roo.bootstrap.Radio} item The checked item
37395         * @param {Roo.EventObject} e The event object
37396         */
37397        click : true
37398     });
37399     
37400 };
37401
37402 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37403
37404     radioes : false,
37405     
37406     inline : true,
37407     
37408     weight : '',
37409     
37410     indicatorpos : 'left',
37411     
37412     getAutoCreate : function()
37413     {
37414         var label = {
37415             tag : 'label',
37416             cls : 'roo-radio-set-label',
37417             cn : [
37418                 {
37419                     tag : 'span',
37420                     html : this.fieldLabel
37421                 }
37422             ]
37423         };
37424         if (Roo.bootstrap.version == 3) {
37425             
37426             
37427             if(this.indicatorpos == 'left'){
37428                 label.cn.unshift({
37429                     tag : 'i',
37430                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37431                     tooltip : 'This field is required'
37432                 });
37433             } else {
37434                 label.cn.push({
37435                     tag : 'i',
37436                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37437                     tooltip : 'This field is required'
37438                 });
37439             }
37440         }
37441         var items = {
37442             tag : 'div',
37443             cls : 'roo-radio-set-items'
37444         };
37445         
37446         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37447         
37448         if (align === 'left' && this.fieldLabel.length) {
37449             
37450             items = {
37451                 cls : "roo-radio-set-right", 
37452                 cn: [
37453                     items
37454                 ]
37455             };
37456             
37457             if(this.labelWidth > 12){
37458                 label.style = "width: " + this.labelWidth + 'px';
37459             }
37460             
37461             if(this.labelWidth < 13 && this.labelmd == 0){
37462                 this.labelmd = this.labelWidth;
37463             }
37464             
37465             if(this.labellg > 0){
37466                 label.cls += ' col-lg-' + this.labellg;
37467                 items.cls += ' col-lg-' + (12 - this.labellg);
37468             }
37469             
37470             if(this.labelmd > 0){
37471                 label.cls += ' col-md-' + this.labelmd;
37472                 items.cls += ' col-md-' + (12 - this.labelmd);
37473             }
37474             
37475             if(this.labelsm > 0){
37476                 label.cls += ' col-sm-' + this.labelsm;
37477                 items.cls += ' col-sm-' + (12 - this.labelsm);
37478             }
37479             
37480             if(this.labelxs > 0){
37481                 label.cls += ' col-xs-' + this.labelxs;
37482                 items.cls += ' col-xs-' + (12 - this.labelxs);
37483             }
37484         }
37485         
37486         var cfg = {
37487             tag : 'div',
37488             cls : 'roo-radio-set',
37489             cn : [
37490                 {
37491                     tag : 'input',
37492                     cls : 'roo-radio-set-input',
37493                     type : 'hidden',
37494                     name : this.name,
37495                     value : this.value ? this.value :  ''
37496                 },
37497                 label,
37498                 items
37499             ]
37500         };
37501         
37502         if(this.weight.length){
37503             cfg.cls += ' roo-radio-' + this.weight;
37504         }
37505         
37506         if(this.inline) {
37507             cfg.cls += ' roo-radio-set-inline';
37508         }
37509         
37510         var settings=this;
37511         ['xs','sm','md','lg'].map(function(size){
37512             if (settings[size]) {
37513                 cfg.cls += ' col-' + size + '-' + settings[size];
37514             }
37515         });
37516         
37517         return cfg;
37518         
37519     },
37520
37521     initEvents : function()
37522     {
37523         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37524         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37525         
37526         if(!this.fieldLabel.length){
37527             this.labelEl.hide();
37528         }
37529         
37530         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37531         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37532         
37533         this.indicator = this.indicatorEl();
37534         
37535         if(this.indicator){
37536             this.indicator.addClass('invisible');
37537         }
37538         
37539         this.originalValue = this.getValue();
37540         
37541     },
37542     
37543     inputEl: function ()
37544     {
37545         return this.el.select('.roo-radio-set-input', true).first();
37546     },
37547     
37548     getChildContainer : function()
37549     {
37550         return this.itemsEl;
37551     },
37552     
37553     register : function(item)
37554     {
37555         this.radioes.push(item);
37556         
37557     },
37558     
37559     validate : function()
37560     {   
37561         if(this.getVisibilityEl().hasClass('hidden')){
37562             return true;
37563         }
37564         
37565         var valid = false;
37566         
37567         Roo.each(this.radioes, function(i){
37568             if(!i.checked){
37569                 return;
37570             }
37571             
37572             valid = true;
37573             return false;
37574         });
37575         
37576         if(this.allowBlank) {
37577             return true;
37578         }
37579         
37580         if(this.disabled || valid){
37581             this.markValid();
37582             return true;
37583         }
37584         
37585         this.markInvalid();
37586         return false;
37587         
37588     },
37589     
37590     markValid : function()
37591     {
37592         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37593             this.indicatorEl().removeClass('visible');
37594             this.indicatorEl().addClass('invisible');
37595         }
37596         
37597         
37598         if (Roo.bootstrap.version == 3) {
37599             this.el.removeClass([this.invalidClass, this.validClass]);
37600             this.el.addClass(this.validClass);
37601         } else {
37602             this.el.removeClass(['is-invalid','is-valid']);
37603             this.el.addClass(['is-valid']);
37604         }
37605         this.fireEvent('valid', this);
37606     },
37607     
37608     markInvalid : function(msg)
37609     {
37610         if(this.allowBlank || this.disabled){
37611             return;
37612         }
37613         
37614         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37615             this.indicatorEl().removeClass('invisible');
37616             this.indicatorEl().addClass('visible');
37617         }
37618         if (Roo.bootstrap.version == 3) {
37619             this.el.removeClass([this.invalidClass, this.validClass]);
37620             this.el.addClass(this.invalidClass);
37621         } else {
37622             this.el.removeClass(['is-invalid','is-valid']);
37623             this.el.addClass(['is-invalid']);
37624         }
37625         
37626         this.fireEvent('invalid', this, msg);
37627         
37628     },
37629     
37630     setValue : function(v, suppressEvent)
37631     {   
37632         if(this.value === v){
37633             return;
37634         }
37635         
37636         this.value = v;
37637         
37638         if(this.rendered){
37639             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37640         }
37641         
37642         Roo.each(this.radioes, function(i){
37643             i.checked = false;
37644             i.el.removeClass('checked');
37645         });
37646         
37647         Roo.each(this.radioes, function(i){
37648             
37649             if(i.value === v || i.value.toString() === v.toString()){
37650                 i.checked = true;
37651                 i.el.addClass('checked');
37652                 
37653                 if(suppressEvent !== true){
37654                     this.fireEvent('check', this, i);
37655                 }
37656                 
37657                 return false;
37658             }
37659             
37660         }, this);
37661         
37662         this.validate();
37663     },
37664     
37665     clearInvalid : function(){
37666         
37667         if(!this.el || this.preventMark){
37668             return;
37669         }
37670         
37671         this.el.removeClass([this.invalidClass]);
37672         
37673         this.fireEvent('valid', this);
37674     }
37675     
37676 });
37677
37678 Roo.apply(Roo.bootstrap.RadioSet, {
37679     
37680     groups: {},
37681     
37682     register : function(set)
37683     {
37684         this.groups[set.name] = set;
37685     },
37686     
37687     get: function(name) 
37688     {
37689         if (typeof(this.groups[name]) == 'undefined') {
37690             return false;
37691         }
37692         
37693         return this.groups[name] ;
37694     }
37695     
37696 });
37697 /*
37698  * Based on:
37699  * Ext JS Library 1.1.1
37700  * Copyright(c) 2006-2007, Ext JS, LLC.
37701  *
37702  * Originally Released Under LGPL - original licence link has changed is not relivant.
37703  *
37704  * Fork - LGPL
37705  * <script type="text/javascript">
37706  */
37707
37708
37709 /**
37710  * @class Roo.bootstrap.SplitBar
37711  * @extends Roo.util.Observable
37712  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37713  * <br><br>
37714  * Usage:
37715  * <pre><code>
37716 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37717                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37718 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37719 split.minSize = 100;
37720 split.maxSize = 600;
37721 split.animate = true;
37722 split.on('moved', splitterMoved);
37723 </code></pre>
37724  * @constructor
37725  * Create a new SplitBar
37726  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37727  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37728  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37729  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37730                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37731                         position of the SplitBar).
37732  */
37733 Roo.bootstrap.SplitBar = function(cfg){
37734     
37735     /** @private */
37736     
37737     //{
37738     //  dragElement : elm
37739     //  resizingElement: el,
37740         // optional..
37741     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37742     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37743         // existingProxy ???
37744     //}
37745     
37746     this.el = Roo.get(cfg.dragElement, true);
37747     this.el.dom.unselectable = "on";
37748     /** @private */
37749     this.resizingEl = Roo.get(cfg.resizingElement, true);
37750
37751     /**
37752      * @private
37753      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37754      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37755      * @type Number
37756      */
37757     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37758     
37759     /**
37760      * The minimum size of the resizing element. (Defaults to 0)
37761      * @type Number
37762      */
37763     this.minSize = 0;
37764     
37765     /**
37766      * The maximum size of the resizing element. (Defaults to 2000)
37767      * @type Number
37768      */
37769     this.maxSize = 2000;
37770     
37771     /**
37772      * Whether to animate the transition to the new size
37773      * @type Boolean
37774      */
37775     this.animate = false;
37776     
37777     /**
37778      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37779      * @type Boolean
37780      */
37781     this.useShim = false;
37782     
37783     /** @private */
37784     this.shim = null;
37785     
37786     if(!cfg.existingProxy){
37787         /** @private */
37788         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37789     }else{
37790         this.proxy = Roo.get(cfg.existingProxy).dom;
37791     }
37792     /** @private */
37793     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37794     
37795     /** @private */
37796     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37797     
37798     /** @private */
37799     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37800     
37801     /** @private */
37802     this.dragSpecs = {};
37803     
37804     /**
37805      * @private The adapter to use to positon and resize elements
37806      */
37807     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37808     this.adapter.init(this);
37809     
37810     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37811         /** @private */
37812         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37813         this.el.addClass("roo-splitbar-h");
37814     }else{
37815         /** @private */
37816         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37817         this.el.addClass("roo-splitbar-v");
37818     }
37819     
37820     this.addEvents({
37821         /**
37822          * @event resize
37823          * Fires when the splitter is moved (alias for {@link #event-moved})
37824          * @param {Roo.bootstrap.SplitBar} this
37825          * @param {Number} newSize the new width or height
37826          */
37827         "resize" : true,
37828         /**
37829          * @event moved
37830          * Fires when the splitter is moved
37831          * @param {Roo.bootstrap.SplitBar} this
37832          * @param {Number} newSize the new width or height
37833          */
37834         "moved" : true,
37835         /**
37836          * @event beforeresize
37837          * Fires before the splitter is dragged
37838          * @param {Roo.bootstrap.SplitBar} this
37839          */
37840         "beforeresize" : true,
37841
37842         "beforeapply" : true
37843     });
37844
37845     Roo.util.Observable.call(this);
37846 };
37847
37848 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37849     onStartProxyDrag : function(x, y){
37850         this.fireEvent("beforeresize", this);
37851         if(!this.overlay){
37852             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37853             o.unselectable();
37854             o.enableDisplayMode("block");
37855             // all splitbars share the same overlay
37856             Roo.bootstrap.SplitBar.prototype.overlay = o;
37857         }
37858         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37859         this.overlay.show();
37860         Roo.get(this.proxy).setDisplayed("block");
37861         var size = this.adapter.getElementSize(this);
37862         this.activeMinSize = this.getMinimumSize();;
37863         this.activeMaxSize = this.getMaximumSize();;
37864         var c1 = size - this.activeMinSize;
37865         var c2 = Math.max(this.activeMaxSize - size, 0);
37866         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37867             this.dd.resetConstraints();
37868             this.dd.setXConstraint(
37869                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37870                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37871             );
37872             this.dd.setYConstraint(0, 0);
37873         }else{
37874             this.dd.resetConstraints();
37875             this.dd.setXConstraint(0, 0);
37876             this.dd.setYConstraint(
37877                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37878                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37879             );
37880          }
37881         this.dragSpecs.startSize = size;
37882         this.dragSpecs.startPoint = [x, y];
37883         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37884     },
37885     
37886     /** 
37887      * @private Called after the drag operation by the DDProxy
37888      */
37889     onEndProxyDrag : function(e){
37890         Roo.get(this.proxy).setDisplayed(false);
37891         var endPoint = Roo.lib.Event.getXY(e);
37892         if(this.overlay){
37893             this.overlay.hide();
37894         }
37895         var newSize;
37896         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37897             newSize = this.dragSpecs.startSize + 
37898                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37899                     endPoint[0] - this.dragSpecs.startPoint[0] :
37900                     this.dragSpecs.startPoint[0] - endPoint[0]
37901                 );
37902         }else{
37903             newSize = this.dragSpecs.startSize + 
37904                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37905                     endPoint[1] - this.dragSpecs.startPoint[1] :
37906                     this.dragSpecs.startPoint[1] - endPoint[1]
37907                 );
37908         }
37909         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37910         if(newSize != this.dragSpecs.startSize){
37911             if(this.fireEvent('beforeapply', this, newSize) !== false){
37912                 this.adapter.setElementSize(this, newSize);
37913                 this.fireEvent("moved", this, newSize);
37914                 this.fireEvent("resize", this, newSize);
37915             }
37916         }
37917     },
37918     
37919     /**
37920      * Get the adapter this SplitBar uses
37921      * @return The adapter object
37922      */
37923     getAdapter : function(){
37924         return this.adapter;
37925     },
37926     
37927     /**
37928      * Set the adapter this SplitBar uses
37929      * @param {Object} adapter A SplitBar adapter object
37930      */
37931     setAdapter : function(adapter){
37932         this.adapter = adapter;
37933         this.adapter.init(this);
37934     },
37935     
37936     /**
37937      * Gets the minimum size for the resizing element
37938      * @return {Number} The minimum size
37939      */
37940     getMinimumSize : function(){
37941         return this.minSize;
37942     },
37943     
37944     /**
37945      * Sets the minimum size for the resizing element
37946      * @param {Number} minSize The minimum size
37947      */
37948     setMinimumSize : function(minSize){
37949         this.minSize = minSize;
37950     },
37951     
37952     /**
37953      * Gets the maximum size for the resizing element
37954      * @return {Number} The maximum size
37955      */
37956     getMaximumSize : function(){
37957         return this.maxSize;
37958     },
37959     
37960     /**
37961      * Sets the maximum size for the resizing element
37962      * @param {Number} maxSize The maximum size
37963      */
37964     setMaximumSize : function(maxSize){
37965         this.maxSize = maxSize;
37966     },
37967     
37968     /**
37969      * Sets the initialize size for the resizing element
37970      * @param {Number} size The initial size
37971      */
37972     setCurrentSize : function(size){
37973         var oldAnimate = this.animate;
37974         this.animate = false;
37975         this.adapter.setElementSize(this, size);
37976         this.animate = oldAnimate;
37977     },
37978     
37979     /**
37980      * Destroy this splitbar. 
37981      * @param {Boolean} removeEl True to remove the element
37982      */
37983     destroy : function(removeEl){
37984         if(this.shim){
37985             this.shim.remove();
37986         }
37987         this.dd.unreg();
37988         this.proxy.parentNode.removeChild(this.proxy);
37989         if(removeEl){
37990             this.el.remove();
37991         }
37992     }
37993 });
37994
37995 /**
37996  * @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.
37997  */
37998 Roo.bootstrap.SplitBar.createProxy = function(dir){
37999     var proxy = new Roo.Element(document.createElement("div"));
38000     proxy.unselectable();
38001     var cls = 'roo-splitbar-proxy';
38002     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38003     document.body.appendChild(proxy.dom);
38004     return proxy.dom;
38005 };
38006
38007 /** 
38008  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38009  * Default Adapter. It assumes the splitter and resizing element are not positioned
38010  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38011  */
38012 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38013 };
38014
38015 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38016     // do nothing for now
38017     init : function(s){
38018     
38019     },
38020     /**
38021      * Called before drag operations to get the current size of the resizing element. 
38022      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38023      */
38024      getElementSize : function(s){
38025         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38026             return s.resizingEl.getWidth();
38027         }else{
38028             return s.resizingEl.getHeight();
38029         }
38030     },
38031     
38032     /**
38033      * Called after drag operations to set the size of the resizing element.
38034      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38035      * @param {Number} newSize The new size to set
38036      * @param {Function} onComplete A function to be invoked when resizing is complete
38037      */
38038     setElementSize : function(s, newSize, onComplete){
38039         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38040             if(!s.animate){
38041                 s.resizingEl.setWidth(newSize);
38042                 if(onComplete){
38043                     onComplete(s, newSize);
38044                 }
38045             }else{
38046                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38047             }
38048         }else{
38049             
38050             if(!s.animate){
38051                 s.resizingEl.setHeight(newSize);
38052                 if(onComplete){
38053                     onComplete(s, newSize);
38054                 }
38055             }else{
38056                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38057             }
38058         }
38059     }
38060 };
38061
38062 /** 
38063  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38064  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38065  * Adapter that  moves the splitter element to align with the resized sizing element. 
38066  * Used with an absolute positioned SplitBar.
38067  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38068  * document.body, make sure you assign an id to the body element.
38069  */
38070 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38071     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38072     this.container = Roo.get(container);
38073 };
38074
38075 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38076     init : function(s){
38077         this.basic.init(s);
38078     },
38079     
38080     getElementSize : function(s){
38081         return this.basic.getElementSize(s);
38082     },
38083     
38084     setElementSize : function(s, newSize, onComplete){
38085         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38086     },
38087     
38088     moveSplitter : function(s){
38089         var yes = Roo.bootstrap.SplitBar;
38090         switch(s.placement){
38091             case yes.LEFT:
38092                 s.el.setX(s.resizingEl.getRight());
38093                 break;
38094             case yes.RIGHT:
38095                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38096                 break;
38097             case yes.TOP:
38098                 s.el.setY(s.resizingEl.getBottom());
38099                 break;
38100             case yes.BOTTOM:
38101                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38102                 break;
38103         }
38104     }
38105 };
38106
38107 /**
38108  * Orientation constant - Create a vertical SplitBar
38109  * @static
38110  * @type Number
38111  */
38112 Roo.bootstrap.SplitBar.VERTICAL = 1;
38113
38114 /**
38115  * Orientation constant - Create a horizontal SplitBar
38116  * @static
38117  * @type Number
38118  */
38119 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38120
38121 /**
38122  * Placement constant - The resizing element is to the left of the splitter element
38123  * @static
38124  * @type Number
38125  */
38126 Roo.bootstrap.SplitBar.LEFT = 1;
38127
38128 /**
38129  * Placement constant - The resizing element is to the right of the splitter element
38130  * @static
38131  * @type Number
38132  */
38133 Roo.bootstrap.SplitBar.RIGHT = 2;
38134
38135 /**
38136  * Placement constant - The resizing element is positioned above the splitter element
38137  * @static
38138  * @type Number
38139  */
38140 Roo.bootstrap.SplitBar.TOP = 3;
38141
38142 /**
38143  * Placement constant - The resizing element is positioned under splitter element
38144  * @static
38145  * @type Number
38146  */
38147 Roo.bootstrap.SplitBar.BOTTOM = 4;
38148 Roo.namespace("Roo.bootstrap.layout");/*
38149  * Based on:
38150  * Ext JS Library 1.1.1
38151  * Copyright(c) 2006-2007, Ext JS, LLC.
38152  *
38153  * Originally Released Under LGPL - original licence link has changed is not relivant.
38154  *
38155  * Fork - LGPL
38156  * <script type="text/javascript">
38157  */
38158
38159 /**
38160  * @class Roo.bootstrap.layout.Manager
38161  * @extends Roo.bootstrap.Component
38162  * Base class for layout managers.
38163  */
38164 Roo.bootstrap.layout.Manager = function(config)
38165 {
38166     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38167
38168
38169
38170
38171
38172     /** false to disable window resize monitoring @type Boolean */
38173     this.monitorWindowResize = true;
38174     this.regions = {};
38175     this.addEvents({
38176         /**
38177          * @event layout
38178          * Fires when a layout is performed.
38179          * @param {Roo.LayoutManager} this
38180          */
38181         "layout" : true,
38182         /**
38183          * @event regionresized
38184          * Fires when the user resizes a region.
38185          * @param {Roo.LayoutRegion} region The resized region
38186          * @param {Number} newSize The new size (width for east/west, height for north/south)
38187          */
38188         "regionresized" : true,
38189         /**
38190          * @event regioncollapsed
38191          * Fires when a region is collapsed.
38192          * @param {Roo.LayoutRegion} region The collapsed region
38193          */
38194         "regioncollapsed" : true,
38195         /**
38196          * @event regionexpanded
38197          * Fires when a region is expanded.
38198          * @param {Roo.LayoutRegion} region The expanded region
38199          */
38200         "regionexpanded" : true
38201     });
38202     this.updating = false;
38203
38204     if (config.el) {
38205         this.el = Roo.get(config.el);
38206         this.initEvents();
38207     }
38208
38209 };
38210
38211 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38212
38213
38214     regions : null,
38215
38216     monitorWindowResize : true,
38217
38218
38219     updating : false,
38220
38221
38222     onRender : function(ct, position)
38223     {
38224         if(!this.el){
38225             this.el = Roo.get(ct);
38226             this.initEvents();
38227         }
38228         //this.fireEvent('render',this);
38229     },
38230
38231
38232     initEvents: function()
38233     {
38234
38235
38236         // ie scrollbar fix
38237         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38238             document.body.scroll = "no";
38239         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38240             this.el.position('relative');
38241         }
38242         this.id = this.el.id;
38243         this.el.addClass("roo-layout-container");
38244         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38245         if(this.el.dom != document.body ) {
38246             this.el.on('resize', this.layout,this);
38247             this.el.on('show', this.layout,this);
38248         }
38249
38250     },
38251
38252     /**
38253      * Returns true if this layout is currently being updated
38254      * @return {Boolean}
38255      */
38256     isUpdating : function(){
38257         return this.updating;
38258     },
38259
38260     /**
38261      * Suspend the LayoutManager from doing auto-layouts while
38262      * making multiple add or remove calls
38263      */
38264     beginUpdate : function(){
38265         this.updating = true;
38266     },
38267
38268     /**
38269      * Restore auto-layouts and optionally disable the manager from performing a layout
38270      * @param {Boolean} noLayout true to disable a layout update
38271      */
38272     endUpdate : function(noLayout){
38273         this.updating = false;
38274         if(!noLayout){
38275             this.layout();
38276         }
38277     },
38278
38279     layout: function(){
38280         // abstract...
38281     },
38282
38283     onRegionResized : function(region, newSize){
38284         this.fireEvent("regionresized", region, newSize);
38285         this.layout();
38286     },
38287
38288     onRegionCollapsed : function(region){
38289         this.fireEvent("regioncollapsed", region);
38290     },
38291
38292     onRegionExpanded : function(region){
38293         this.fireEvent("regionexpanded", region);
38294     },
38295
38296     /**
38297      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38298      * performs box-model adjustments.
38299      * @return {Object} The size as an object {width: (the width), height: (the height)}
38300      */
38301     getViewSize : function()
38302     {
38303         var size;
38304         if(this.el.dom != document.body){
38305             size = this.el.getSize();
38306         }else{
38307             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38308         }
38309         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38310         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38311         return size;
38312     },
38313
38314     /**
38315      * Returns the Element this layout is bound to.
38316      * @return {Roo.Element}
38317      */
38318     getEl : function(){
38319         return this.el;
38320     },
38321
38322     /**
38323      * Returns the specified region.
38324      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38325      * @return {Roo.LayoutRegion}
38326      */
38327     getRegion : function(target){
38328         return this.regions[target.toLowerCase()];
38329     },
38330
38331     onWindowResize : function(){
38332         if(this.monitorWindowResize){
38333             this.layout();
38334         }
38335     }
38336 });
38337 /*
38338  * Based on:
38339  * Ext JS Library 1.1.1
38340  * Copyright(c) 2006-2007, Ext JS, LLC.
38341  *
38342  * Originally Released Under LGPL - original licence link has changed is not relivant.
38343  *
38344  * Fork - LGPL
38345  * <script type="text/javascript">
38346  */
38347 /**
38348  * @class Roo.bootstrap.layout.Border
38349  * @extends Roo.bootstrap.layout.Manager
38350  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38351  * please see: examples/bootstrap/nested.html<br><br>
38352  
38353 <b>The container the layout is rendered into can be either the body element or any other element.
38354 If it is not the body element, the container needs to either be an absolute positioned element,
38355 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38356 the container size if it is not the body element.</b>
38357
38358 * @constructor
38359 * Create a new Border
38360 * @param {Object} config Configuration options
38361  */
38362 Roo.bootstrap.layout.Border = function(config){
38363     config = config || {};
38364     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38365     
38366     
38367     
38368     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38369         if(config[region]){
38370             config[region].region = region;
38371             this.addRegion(config[region]);
38372         }
38373     },this);
38374     
38375 };
38376
38377 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38378
38379 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38380     
38381     parent : false, // this might point to a 'nest' or a ???
38382     
38383     /**
38384      * Creates and adds a new region if it doesn't already exist.
38385      * @param {String} target The target region key (north, south, east, west or center).
38386      * @param {Object} config The regions config object
38387      * @return {BorderLayoutRegion} The new region
38388      */
38389     addRegion : function(config)
38390     {
38391         if(!this.regions[config.region]){
38392             var r = this.factory(config);
38393             this.bindRegion(r);
38394         }
38395         return this.regions[config.region];
38396     },
38397
38398     // private (kinda)
38399     bindRegion : function(r){
38400         this.regions[r.config.region] = r;
38401         
38402         r.on("visibilitychange",    this.layout, this);
38403         r.on("paneladded",          this.layout, this);
38404         r.on("panelremoved",        this.layout, this);
38405         r.on("invalidated",         this.layout, this);
38406         r.on("resized",             this.onRegionResized, this);
38407         r.on("collapsed",           this.onRegionCollapsed, this);
38408         r.on("expanded",            this.onRegionExpanded, this);
38409     },
38410
38411     /**
38412      * Performs a layout update.
38413      */
38414     layout : function()
38415     {
38416         if(this.updating) {
38417             return;
38418         }
38419         
38420         // render all the rebions if they have not been done alreayd?
38421         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38422             if(this.regions[region] && !this.regions[region].bodyEl){
38423                 this.regions[region].onRender(this.el)
38424             }
38425         },this);
38426         
38427         var size = this.getViewSize();
38428         var w = size.width;
38429         var h = size.height;
38430         var centerW = w;
38431         var centerH = h;
38432         var centerY = 0;
38433         var centerX = 0;
38434         //var x = 0, y = 0;
38435
38436         var rs = this.regions;
38437         var north = rs["north"];
38438         var south = rs["south"]; 
38439         var west = rs["west"];
38440         var east = rs["east"];
38441         var center = rs["center"];
38442         //if(this.hideOnLayout){ // not supported anymore
38443             //c.el.setStyle("display", "none");
38444         //}
38445         if(north && north.isVisible()){
38446             var b = north.getBox();
38447             var m = north.getMargins();
38448             b.width = w - (m.left+m.right);
38449             b.x = m.left;
38450             b.y = m.top;
38451             centerY = b.height + b.y + m.bottom;
38452             centerH -= centerY;
38453             north.updateBox(this.safeBox(b));
38454         }
38455         if(south && south.isVisible()){
38456             var b = south.getBox();
38457             var m = south.getMargins();
38458             b.width = w - (m.left+m.right);
38459             b.x = m.left;
38460             var totalHeight = (b.height + m.top + m.bottom);
38461             b.y = h - totalHeight + m.top;
38462             centerH -= totalHeight;
38463             south.updateBox(this.safeBox(b));
38464         }
38465         if(west && west.isVisible()){
38466             var b = west.getBox();
38467             var m = west.getMargins();
38468             b.height = centerH - (m.top+m.bottom);
38469             b.x = m.left;
38470             b.y = centerY + m.top;
38471             var totalWidth = (b.width + m.left + m.right);
38472             centerX += totalWidth;
38473             centerW -= totalWidth;
38474             west.updateBox(this.safeBox(b));
38475         }
38476         if(east && east.isVisible()){
38477             var b = east.getBox();
38478             var m = east.getMargins();
38479             b.height = centerH - (m.top+m.bottom);
38480             var totalWidth = (b.width + m.left + m.right);
38481             b.x = w - totalWidth + m.left;
38482             b.y = centerY + m.top;
38483             centerW -= totalWidth;
38484             east.updateBox(this.safeBox(b));
38485         }
38486         if(center){
38487             var m = center.getMargins();
38488             var centerBox = {
38489                 x: centerX + m.left,
38490                 y: centerY + m.top,
38491                 width: centerW - (m.left+m.right),
38492                 height: centerH - (m.top+m.bottom)
38493             };
38494             //if(this.hideOnLayout){
38495                 //center.el.setStyle("display", "block");
38496             //}
38497             center.updateBox(this.safeBox(centerBox));
38498         }
38499         this.el.repaint();
38500         this.fireEvent("layout", this);
38501     },
38502
38503     // private
38504     safeBox : function(box){
38505         box.width = Math.max(0, box.width);
38506         box.height = Math.max(0, box.height);
38507         return box;
38508     },
38509
38510     /**
38511      * Adds a ContentPanel (or subclass) to this layout.
38512      * @param {String} target The target region key (north, south, east, west or center).
38513      * @param {Roo.ContentPanel} panel The panel to add
38514      * @return {Roo.ContentPanel} The added panel
38515      */
38516     add : function(target, panel){
38517          
38518         target = target.toLowerCase();
38519         return this.regions[target].add(panel);
38520     },
38521
38522     /**
38523      * Remove a ContentPanel (or subclass) to this layout.
38524      * @param {String} target The target region key (north, south, east, west or center).
38525      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38526      * @return {Roo.ContentPanel} The removed panel
38527      */
38528     remove : function(target, panel){
38529         target = target.toLowerCase();
38530         return this.regions[target].remove(panel);
38531     },
38532
38533     /**
38534      * Searches all regions for a panel with the specified id
38535      * @param {String} panelId
38536      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38537      */
38538     findPanel : function(panelId){
38539         var rs = this.regions;
38540         for(var target in rs){
38541             if(typeof rs[target] != "function"){
38542                 var p = rs[target].getPanel(panelId);
38543                 if(p){
38544                     return p;
38545                 }
38546             }
38547         }
38548         return null;
38549     },
38550
38551     /**
38552      * Searches all regions for a panel with the specified id and activates (shows) it.
38553      * @param {String/ContentPanel} panelId The panels id or the panel itself
38554      * @return {Roo.ContentPanel} The shown panel or null
38555      */
38556     showPanel : function(panelId) {
38557       var rs = this.regions;
38558       for(var target in rs){
38559          var r = rs[target];
38560          if(typeof r != "function"){
38561             if(r.hasPanel(panelId)){
38562                return r.showPanel(panelId);
38563             }
38564          }
38565       }
38566       return null;
38567    },
38568
38569    /**
38570      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38571      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38572      */
38573    /*
38574     restoreState : function(provider){
38575         if(!provider){
38576             provider = Roo.state.Manager;
38577         }
38578         var sm = new Roo.LayoutStateManager();
38579         sm.init(this, provider);
38580     },
38581 */
38582  
38583  
38584     /**
38585      * Adds a xtype elements to the layout.
38586      * <pre><code>
38587
38588 layout.addxtype({
38589        xtype : 'ContentPanel',
38590        region: 'west',
38591        items: [ .... ]
38592    }
38593 );
38594
38595 layout.addxtype({
38596         xtype : 'NestedLayoutPanel',
38597         region: 'west',
38598         layout: {
38599            center: { },
38600            west: { }   
38601         },
38602         items : [ ... list of content panels or nested layout panels.. ]
38603    }
38604 );
38605 </code></pre>
38606      * @param {Object} cfg Xtype definition of item to add.
38607      */
38608     addxtype : function(cfg)
38609     {
38610         // basically accepts a pannel...
38611         // can accept a layout region..!?!?
38612         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38613         
38614         
38615         // theory?  children can only be panels??
38616         
38617         //if (!cfg.xtype.match(/Panel$/)) {
38618         //    return false;
38619         //}
38620         var ret = false;
38621         
38622         if (typeof(cfg.region) == 'undefined') {
38623             Roo.log("Failed to add Panel, region was not set");
38624             Roo.log(cfg);
38625             return false;
38626         }
38627         var region = cfg.region;
38628         delete cfg.region;
38629         
38630           
38631         var xitems = [];
38632         if (cfg.items) {
38633             xitems = cfg.items;
38634             delete cfg.items;
38635         }
38636         var nb = false;
38637         
38638         if ( region == 'center') {
38639             Roo.log("Center: " + cfg.title);
38640         }
38641         
38642         
38643         switch(cfg.xtype) 
38644         {
38645             case 'Content':  // ContentPanel (el, cfg)
38646             case 'Scroll':  // ContentPanel (el, cfg)
38647             case 'View': 
38648                 cfg.autoCreate = cfg.autoCreate || true;
38649                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38650                 //} else {
38651                 //    var el = this.el.createChild();
38652                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38653                 //}
38654                 
38655                 this.add(region, ret);
38656                 break;
38657             
38658             /*
38659             case 'TreePanel': // our new panel!
38660                 cfg.el = this.el.createChild();
38661                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38662                 this.add(region, ret);
38663                 break;
38664             */
38665             
38666             case 'Nest': 
38667                 // create a new Layout (which is  a Border Layout...
38668                 
38669                 var clayout = cfg.layout;
38670                 clayout.el  = this.el.createChild();
38671                 clayout.items   = clayout.items  || [];
38672                 
38673                 delete cfg.layout;
38674                 
38675                 // replace this exitems with the clayout ones..
38676                 xitems = clayout.items;
38677                  
38678                 // force background off if it's in center...
38679                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38680                     cfg.background = false;
38681                 }
38682                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38683                 
38684                 
38685                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38686                 //console.log('adding nested layout panel '  + cfg.toSource());
38687                 this.add(region, ret);
38688                 nb = {}; /// find first...
38689                 break;
38690             
38691             case 'Grid':
38692                 
38693                 // needs grid and region
38694                 
38695                 //var el = this.getRegion(region).el.createChild();
38696                 /*
38697                  *var el = this.el.createChild();
38698                 // create the grid first...
38699                 cfg.grid.container = el;
38700                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38701                 */
38702                 
38703                 if (region == 'center' && this.active ) {
38704                     cfg.background = false;
38705                 }
38706                 
38707                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38708                 
38709                 this.add(region, ret);
38710                 /*
38711                 if (cfg.background) {
38712                     // render grid on panel activation (if panel background)
38713                     ret.on('activate', function(gp) {
38714                         if (!gp.grid.rendered) {
38715                     //        gp.grid.render(el);
38716                         }
38717                     });
38718                 } else {
38719                   //  cfg.grid.render(el);
38720                 }
38721                 */
38722                 break;
38723            
38724            
38725             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38726                 // it was the old xcomponent building that caused this before.
38727                 // espeically if border is the top element in the tree.
38728                 ret = this;
38729                 break; 
38730                 
38731                     
38732                 
38733                 
38734                 
38735             default:
38736                 /*
38737                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38738                     
38739                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38740                     this.add(region, ret);
38741                 } else {
38742                 */
38743                     Roo.log(cfg);
38744                     throw "Can not add '" + cfg.xtype + "' to Border";
38745                     return null;
38746              
38747                                 
38748              
38749         }
38750         this.beginUpdate();
38751         // add children..
38752         var region = '';
38753         var abn = {};
38754         Roo.each(xitems, function(i)  {
38755             region = nb && i.region ? i.region : false;
38756             
38757             var add = ret.addxtype(i);
38758            
38759             if (region) {
38760                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38761                 if (!i.background) {
38762                     abn[region] = nb[region] ;
38763                 }
38764             }
38765             
38766         });
38767         this.endUpdate();
38768
38769         // make the last non-background panel active..
38770         //if (nb) { Roo.log(abn); }
38771         if (nb) {
38772             
38773             for(var r in abn) {
38774                 region = this.getRegion(r);
38775                 if (region) {
38776                     // tried using nb[r], but it does not work..
38777                      
38778                     region.showPanel(abn[r]);
38779                    
38780                 }
38781             }
38782         }
38783         return ret;
38784         
38785     },
38786     
38787     
38788 // private
38789     factory : function(cfg)
38790     {
38791         
38792         var validRegions = Roo.bootstrap.layout.Border.regions;
38793
38794         var target = cfg.region;
38795         cfg.mgr = this;
38796         
38797         var r = Roo.bootstrap.layout;
38798         Roo.log(target);
38799         switch(target){
38800             case "north":
38801                 return new r.North(cfg);
38802             case "south":
38803                 return new r.South(cfg);
38804             case "east":
38805                 return new r.East(cfg);
38806             case "west":
38807                 return new r.West(cfg);
38808             case "center":
38809                 return new r.Center(cfg);
38810         }
38811         throw 'Layout region "'+target+'" not supported.';
38812     }
38813     
38814     
38815 });
38816  /*
38817  * Based on:
38818  * Ext JS Library 1.1.1
38819  * Copyright(c) 2006-2007, Ext JS, LLC.
38820  *
38821  * Originally Released Under LGPL - original licence link has changed is not relivant.
38822  *
38823  * Fork - LGPL
38824  * <script type="text/javascript">
38825  */
38826  
38827 /**
38828  * @class Roo.bootstrap.layout.Basic
38829  * @extends Roo.util.Observable
38830  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38831  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38832  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38833  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38834  * @cfg {string}   region  the region that it inhabits..
38835  * @cfg {bool}   skipConfig skip config?
38836  * 
38837
38838  */
38839 Roo.bootstrap.layout.Basic = function(config){
38840     
38841     this.mgr = config.mgr;
38842     
38843     this.position = config.region;
38844     
38845     var skipConfig = config.skipConfig;
38846     
38847     this.events = {
38848         /**
38849          * @scope Roo.BasicLayoutRegion
38850          */
38851         
38852         /**
38853          * @event beforeremove
38854          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38855          * @param {Roo.LayoutRegion} this
38856          * @param {Roo.ContentPanel} panel The panel
38857          * @param {Object} e The cancel event object
38858          */
38859         "beforeremove" : true,
38860         /**
38861          * @event invalidated
38862          * Fires when the layout for this region is changed.
38863          * @param {Roo.LayoutRegion} this
38864          */
38865         "invalidated" : true,
38866         /**
38867          * @event visibilitychange
38868          * Fires when this region is shown or hidden 
38869          * @param {Roo.LayoutRegion} this
38870          * @param {Boolean} visibility true or false
38871          */
38872         "visibilitychange" : true,
38873         /**
38874          * @event paneladded
38875          * Fires when a panel is added. 
38876          * @param {Roo.LayoutRegion} this
38877          * @param {Roo.ContentPanel} panel The panel
38878          */
38879         "paneladded" : true,
38880         /**
38881          * @event panelremoved
38882          * Fires when a panel is removed. 
38883          * @param {Roo.LayoutRegion} this
38884          * @param {Roo.ContentPanel} panel The panel
38885          */
38886         "panelremoved" : true,
38887         /**
38888          * @event beforecollapse
38889          * Fires when this region before collapse.
38890          * @param {Roo.LayoutRegion} this
38891          */
38892         "beforecollapse" : true,
38893         /**
38894          * @event collapsed
38895          * Fires when this region is collapsed.
38896          * @param {Roo.LayoutRegion} this
38897          */
38898         "collapsed" : true,
38899         /**
38900          * @event expanded
38901          * Fires when this region is expanded.
38902          * @param {Roo.LayoutRegion} this
38903          */
38904         "expanded" : true,
38905         /**
38906          * @event slideshow
38907          * Fires when this region is slid into view.
38908          * @param {Roo.LayoutRegion} this
38909          */
38910         "slideshow" : true,
38911         /**
38912          * @event slidehide
38913          * Fires when this region slides out of view. 
38914          * @param {Roo.LayoutRegion} this
38915          */
38916         "slidehide" : true,
38917         /**
38918          * @event panelactivated
38919          * Fires when a panel is activated. 
38920          * @param {Roo.LayoutRegion} this
38921          * @param {Roo.ContentPanel} panel The activated panel
38922          */
38923         "panelactivated" : true,
38924         /**
38925          * @event resized
38926          * Fires when the user resizes this region. 
38927          * @param {Roo.LayoutRegion} this
38928          * @param {Number} newSize The new size (width for east/west, height for north/south)
38929          */
38930         "resized" : true
38931     };
38932     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38933     this.panels = new Roo.util.MixedCollection();
38934     this.panels.getKey = this.getPanelId.createDelegate(this);
38935     this.box = null;
38936     this.activePanel = null;
38937     // ensure listeners are added...
38938     
38939     if (config.listeners || config.events) {
38940         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38941             listeners : config.listeners || {},
38942             events : config.events || {}
38943         });
38944     }
38945     
38946     if(skipConfig !== true){
38947         this.applyConfig(config);
38948     }
38949 };
38950
38951 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38952 {
38953     getPanelId : function(p){
38954         return p.getId();
38955     },
38956     
38957     applyConfig : function(config){
38958         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38959         this.config = config;
38960         
38961     },
38962     
38963     /**
38964      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38965      * the width, for horizontal (north, south) the height.
38966      * @param {Number} newSize The new width or height
38967      */
38968     resizeTo : function(newSize){
38969         var el = this.el ? this.el :
38970                  (this.activePanel ? this.activePanel.getEl() : null);
38971         if(el){
38972             switch(this.position){
38973                 case "east":
38974                 case "west":
38975                     el.setWidth(newSize);
38976                     this.fireEvent("resized", this, newSize);
38977                 break;
38978                 case "north":
38979                 case "south":
38980                     el.setHeight(newSize);
38981                     this.fireEvent("resized", this, newSize);
38982                 break;                
38983             }
38984         }
38985     },
38986     
38987     getBox : function(){
38988         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38989     },
38990     
38991     getMargins : function(){
38992         return this.margins;
38993     },
38994     
38995     updateBox : function(box){
38996         this.box = box;
38997         var el = this.activePanel.getEl();
38998         el.dom.style.left = box.x + "px";
38999         el.dom.style.top = box.y + "px";
39000         this.activePanel.setSize(box.width, box.height);
39001     },
39002     
39003     /**
39004      * Returns the container element for this region.
39005      * @return {Roo.Element}
39006      */
39007     getEl : function(){
39008         return this.activePanel;
39009     },
39010     
39011     /**
39012      * Returns true if this region is currently visible.
39013      * @return {Boolean}
39014      */
39015     isVisible : function(){
39016         return this.activePanel ? true : false;
39017     },
39018     
39019     setActivePanel : function(panel){
39020         panel = this.getPanel(panel);
39021         if(this.activePanel && this.activePanel != panel){
39022             this.activePanel.setActiveState(false);
39023             this.activePanel.getEl().setLeftTop(-10000,-10000);
39024         }
39025         this.activePanel = panel;
39026         panel.setActiveState(true);
39027         if(this.box){
39028             panel.setSize(this.box.width, this.box.height);
39029         }
39030         this.fireEvent("panelactivated", this, panel);
39031         this.fireEvent("invalidated");
39032     },
39033     
39034     /**
39035      * Show the specified panel.
39036      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39037      * @return {Roo.ContentPanel} The shown panel or null
39038      */
39039     showPanel : function(panel){
39040         panel = this.getPanel(panel);
39041         if(panel){
39042             this.setActivePanel(panel);
39043         }
39044         return panel;
39045     },
39046     
39047     /**
39048      * Get the active panel for this region.
39049      * @return {Roo.ContentPanel} The active panel or null
39050      */
39051     getActivePanel : function(){
39052         return this.activePanel;
39053     },
39054     
39055     /**
39056      * Add the passed ContentPanel(s)
39057      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39058      * @return {Roo.ContentPanel} The panel added (if only one was added)
39059      */
39060     add : function(panel){
39061         if(arguments.length > 1){
39062             for(var i = 0, len = arguments.length; i < len; i++) {
39063                 this.add(arguments[i]);
39064             }
39065             return null;
39066         }
39067         if(this.hasPanel(panel)){
39068             this.showPanel(panel);
39069             return panel;
39070         }
39071         var el = panel.getEl();
39072         if(el.dom.parentNode != this.mgr.el.dom){
39073             this.mgr.el.dom.appendChild(el.dom);
39074         }
39075         if(panel.setRegion){
39076             panel.setRegion(this);
39077         }
39078         this.panels.add(panel);
39079         el.setStyle("position", "absolute");
39080         if(!panel.background){
39081             this.setActivePanel(panel);
39082             if(this.config.initialSize && this.panels.getCount()==1){
39083                 this.resizeTo(this.config.initialSize);
39084             }
39085         }
39086         this.fireEvent("paneladded", this, panel);
39087         return panel;
39088     },
39089     
39090     /**
39091      * Returns true if the panel is in this region.
39092      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39093      * @return {Boolean}
39094      */
39095     hasPanel : function(panel){
39096         if(typeof panel == "object"){ // must be panel obj
39097             panel = panel.getId();
39098         }
39099         return this.getPanel(panel) ? true : false;
39100     },
39101     
39102     /**
39103      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39104      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39105      * @param {Boolean} preservePanel Overrides the config preservePanel option
39106      * @return {Roo.ContentPanel} The panel that was removed
39107      */
39108     remove : function(panel, preservePanel){
39109         panel = this.getPanel(panel);
39110         if(!panel){
39111             return null;
39112         }
39113         var e = {};
39114         this.fireEvent("beforeremove", this, panel, e);
39115         if(e.cancel === true){
39116             return null;
39117         }
39118         var panelId = panel.getId();
39119         this.panels.removeKey(panelId);
39120         return panel;
39121     },
39122     
39123     /**
39124      * Returns the panel specified or null if it's not in this region.
39125      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39126      * @return {Roo.ContentPanel}
39127      */
39128     getPanel : function(id){
39129         if(typeof id == "object"){ // must be panel obj
39130             return id;
39131         }
39132         return this.panels.get(id);
39133     },
39134     
39135     /**
39136      * Returns this regions position (north/south/east/west/center).
39137      * @return {String} 
39138      */
39139     getPosition: function(){
39140         return this.position;    
39141     }
39142 });/*
39143  * Based on:
39144  * Ext JS Library 1.1.1
39145  * Copyright(c) 2006-2007, Ext JS, LLC.
39146  *
39147  * Originally Released Under LGPL - original licence link has changed is not relivant.
39148  *
39149  * Fork - LGPL
39150  * <script type="text/javascript">
39151  */
39152  
39153 /**
39154  * @class Roo.bootstrap.layout.Region
39155  * @extends Roo.bootstrap.layout.Basic
39156  * This class represents a region in a layout manager.
39157  
39158  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39159  * @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})
39160  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39161  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39162  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39163  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39164  * @cfg {String}    title           The title for the region (overrides panel titles)
39165  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39166  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39167  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39168  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39169  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39170  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39171  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39172  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39173  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39174  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39175
39176  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39177  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39178  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39179  * @cfg {Number}    width           For East/West panels
39180  * @cfg {Number}    height          For North/South panels
39181  * @cfg {Boolean}   split           To show the splitter
39182  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39183  * 
39184  * @cfg {string}   cls             Extra CSS classes to add to region
39185  * 
39186  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39187  * @cfg {string}   region  the region that it inhabits..
39188  *
39189
39190  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39191  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39192
39193  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39194  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39195  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39196  */
39197 Roo.bootstrap.layout.Region = function(config)
39198 {
39199     this.applyConfig(config);
39200
39201     var mgr = config.mgr;
39202     var pos = config.region;
39203     config.skipConfig = true;
39204     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39205     
39206     if (mgr.el) {
39207         this.onRender(mgr.el);   
39208     }
39209      
39210     this.visible = true;
39211     this.collapsed = false;
39212     this.unrendered_panels = [];
39213 };
39214
39215 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39216
39217     position: '', // set by wrapper (eg. north/south etc..)
39218     unrendered_panels : null,  // unrendered panels.
39219     
39220     tabPosition : false,
39221     
39222     mgr: false, // points to 'Border'
39223     
39224     
39225     createBody : function(){
39226         /** This region's body element 
39227         * @type Roo.Element */
39228         this.bodyEl = this.el.createChild({
39229                 tag: "div",
39230                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39231         });
39232     },
39233
39234     onRender: function(ctr, pos)
39235     {
39236         var dh = Roo.DomHelper;
39237         /** This region's container element 
39238         * @type Roo.Element */
39239         this.el = dh.append(ctr.dom, {
39240                 tag: "div",
39241                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39242             }, true);
39243         /** This region's title element 
39244         * @type Roo.Element */
39245     
39246         this.titleEl = dh.append(this.el.dom,  {
39247                 tag: "div",
39248                 unselectable: "on",
39249                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39250                 children:[
39251                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39252                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39253                 ]
39254             }, true);
39255         
39256         this.titleEl.enableDisplayMode();
39257         /** This region's title text element 
39258         * @type HTMLElement */
39259         this.titleTextEl = this.titleEl.dom.firstChild;
39260         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39261         /*
39262         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39263         this.closeBtn.enableDisplayMode();
39264         this.closeBtn.on("click", this.closeClicked, this);
39265         this.closeBtn.hide();
39266     */
39267         this.createBody(this.config);
39268         if(this.config.hideWhenEmpty){
39269             this.hide();
39270             this.on("paneladded", this.validateVisibility, this);
39271             this.on("panelremoved", this.validateVisibility, this);
39272         }
39273         if(this.autoScroll){
39274             this.bodyEl.setStyle("overflow", "auto");
39275         }else{
39276             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39277         }
39278         //if(c.titlebar !== false){
39279             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39280                 this.titleEl.hide();
39281             }else{
39282                 this.titleEl.show();
39283                 if(this.config.title){
39284                     this.titleTextEl.innerHTML = this.config.title;
39285                 }
39286             }
39287         //}
39288         if(this.config.collapsed){
39289             this.collapse(true);
39290         }
39291         if(this.config.hidden){
39292             this.hide();
39293         }
39294         
39295         if (this.unrendered_panels && this.unrendered_panels.length) {
39296             for (var i =0;i< this.unrendered_panels.length; i++) {
39297                 this.add(this.unrendered_panels[i]);
39298             }
39299             this.unrendered_panels = null;
39300             
39301         }
39302         
39303     },
39304     
39305     applyConfig : function(c)
39306     {
39307         /*
39308          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39309             var dh = Roo.DomHelper;
39310             if(c.titlebar !== false){
39311                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39312                 this.collapseBtn.on("click", this.collapse, this);
39313                 this.collapseBtn.enableDisplayMode();
39314                 /*
39315                 if(c.showPin === true || this.showPin){
39316                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39317                     this.stickBtn.enableDisplayMode();
39318                     this.stickBtn.on("click", this.expand, this);
39319                     this.stickBtn.hide();
39320                 }
39321                 
39322             }
39323             */
39324             /** This region's collapsed element
39325             * @type Roo.Element */
39326             /*
39327              *
39328             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39329                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39330             ]}, true);
39331             
39332             if(c.floatable !== false){
39333                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39334                this.collapsedEl.on("click", this.collapseClick, this);
39335             }
39336
39337             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39338                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39339                    id: "message", unselectable: "on", style:{"float":"left"}});
39340                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39341              }
39342             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39343             this.expandBtn.on("click", this.expand, this);
39344             
39345         }
39346         
39347         if(this.collapseBtn){
39348             this.collapseBtn.setVisible(c.collapsible == true);
39349         }
39350         
39351         this.cmargins = c.cmargins || this.cmargins ||
39352                          (this.position == "west" || this.position == "east" ?
39353                              {top: 0, left: 2, right:2, bottom: 0} :
39354                              {top: 2, left: 0, right:0, bottom: 2});
39355         */
39356         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39357         
39358         
39359         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39360         
39361         this.autoScroll = c.autoScroll || false;
39362         
39363         
39364        
39365         
39366         this.duration = c.duration || .30;
39367         this.slideDuration = c.slideDuration || .45;
39368         this.config = c;
39369        
39370     },
39371     /**
39372      * Returns true if this region is currently visible.
39373      * @return {Boolean}
39374      */
39375     isVisible : function(){
39376         return this.visible;
39377     },
39378
39379     /**
39380      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39381      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39382      */
39383     //setCollapsedTitle : function(title){
39384     //    title = title || "&#160;";
39385      //   if(this.collapsedTitleTextEl){
39386       //      this.collapsedTitleTextEl.innerHTML = title;
39387        // }
39388     //},
39389
39390     getBox : function(){
39391         var b;
39392       //  if(!this.collapsed){
39393             b = this.el.getBox(false, true);
39394        // }else{
39395           //  b = this.collapsedEl.getBox(false, true);
39396         //}
39397         return b;
39398     },
39399
39400     getMargins : function(){
39401         return this.margins;
39402         //return this.collapsed ? this.cmargins : this.margins;
39403     },
39404 /*
39405     highlight : function(){
39406         this.el.addClass("x-layout-panel-dragover");
39407     },
39408
39409     unhighlight : function(){
39410         this.el.removeClass("x-layout-panel-dragover");
39411     },
39412 */
39413     updateBox : function(box)
39414     {
39415         if (!this.bodyEl) {
39416             return; // not rendered yet..
39417         }
39418         
39419         this.box = box;
39420         if(!this.collapsed){
39421             this.el.dom.style.left = box.x + "px";
39422             this.el.dom.style.top = box.y + "px";
39423             this.updateBody(box.width, box.height);
39424         }else{
39425             this.collapsedEl.dom.style.left = box.x + "px";
39426             this.collapsedEl.dom.style.top = box.y + "px";
39427             this.collapsedEl.setSize(box.width, box.height);
39428         }
39429         if(this.tabs){
39430             this.tabs.autoSizeTabs();
39431         }
39432     },
39433
39434     updateBody : function(w, h)
39435     {
39436         if(w !== null){
39437             this.el.setWidth(w);
39438             w -= this.el.getBorderWidth("rl");
39439             if(this.config.adjustments){
39440                 w += this.config.adjustments[0];
39441             }
39442         }
39443         if(h !== null && h > 0){
39444             this.el.setHeight(h);
39445             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39446             h -= this.el.getBorderWidth("tb");
39447             if(this.config.adjustments){
39448                 h += this.config.adjustments[1];
39449             }
39450             this.bodyEl.setHeight(h);
39451             if(this.tabs){
39452                 h = this.tabs.syncHeight(h);
39453             }
39454         }
39455         if(this.panelSize){
39456             w = w !== null ? w : this.panelSize.width;
39457             h = h !== null ? h : this.panelSize.height;
39458         }
39459         if(this.activePanel){
39460             var el = this.activePanel.getEl();
39461             w = w !== null ? w : el.getWidth();
39462             h = h !== null ? h : el.getHeight();
39463             this.panelSize = {width: w, height: h};
39464             this.activePanel.setSize(w, h);
39465         }
39466         if(Roo.isIE && this.tabs){
39467             this.tabs.el.repaint();
39468         }
39469     },
39470
39471     /**
39472      * Returns the container element for this region.
39473      * @return {Roo.Element}
39474      */
39475     getEl : function(){
39476         return this.el;
39477     },
39478
39479     /**
39480      * Hides this region.
39481      */
39482     hide : function(){
39483         //if(!this.collapsed){
39484             this.el.dom.style.left = "-2000px";
39485             this.el.hide();
39486         //}else{
39487          //   this.collapsedEl.dom.style.left = "-2000px";
39488          //   this.collapsedEl.hide();
39489        // }
39490         this.visible = false;
39491         this.fireEvent("visibilitychange", this, false);
39492     },
39493
39494     /**
39495      * Shows this region if it was previously hidden.
39496      */
39497     show : function(){
39498         //if(!this.collapsed){
39499             this.el.show();
39500         //}else{
39501         //    this.collapsedEl.show();
39502        // }
39503         this.visible = true;
39504         this.fireEvent("visibilitychange", this, true);
39505     },
39506 /*
39507     closeClicked : function(){
39508         if(this.activePanel){
39509             this.remove(this.activePanel);
39510         }
39511     },
39512
39513     collapseClick : function(e){
39514         if(this.isSlid){
39515            e.stopPropagation();
39516            this.slideIn();
39517         }else{
39518            e.stopPropagation();
39519            this.slideOut();
39520         }
39521     },
39522 */
39523     /**
39524      * Collapses this region.
39525      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39526      */
39527     /*
39528     collapse : function(skipAnim, skipCheck = false){
39529         if(this.collapsed) {
39530             return;
39531         }
39532         
39533         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39534             
39535             this.collapsed = true;
39536             if(this.split){
39537                 this.split.el.hide();
39538             }
39539             if(this.config.animate && skipAnim !== true){
39540                 this.fireEvent("invalidated", this);
39541                 this.animateCollapse();
39542             }else{
39543                 this.el.setLocation(-20000,-20000);
39544                 this.el.hide();
39545                 this.collapsedEl.show();
39546                 this.fireEvent("collapsed", this);
39547                 this.fireEvent("invalidated", this);
39548             }
39549         }
39550         
39551     },
39552 */
39553     animateCollapse : function(){
39554         // overridden
39555     },
39556
39557     /**
39558      * Expands this region if it was previously collapsed.
39559      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39560      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39561      */
39562     /*
39563     expand : function(e, skipAnim){
39564         if(e) {
39565             e.stopPropagation();
39566         }
39567         if(!this.collapsed || this.el.hasActiveFx()) {
39568             return;
39569         }
39570         if(this.isSlid){
39571             this.afterSlideIn();
39572             skipAnim = true;
39573         }
39574         this.collapsed = false;
39575         if(this.config.animate && skipAnim !== true){
39576             this.animateExpand();
39577         }else{
39578             this.el.show();
39579             if(this.split){
39580                 this.split.el.show();
39581             }
39582             this.collapsedEl.setLocation(-2000,-2000);
39583             this.collapsedEl.hide();
39584             this.fireEvent("invalidated", this);
39585             this.fireEvent("expanded", this);
39586         }
39587     },
39588 */
39589     animateExpand : function(){
39590         // overridden
39591     },
39592
39593     initTabs : function()
39594     {
39595         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39596         
39597         var ts = new Roo.bootstrap.panel.Tabs({
39598             el: this.bodyEl.dom,
39599             region : this,
39600             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39601             disableTooltips: this.config.disableTabTips,
39602             toolbar : this.config.toolbar
39603         });
39604         
39605         if(this.config.hideTabs){
39606             ts.stripWrap.setDisplayed(false);
39607         }
39608         this.tabs = ts;
39609         ts.resizeTabs = this.config.resizeTabs === true;
39610         ts.minTabWidth = this.config.minTabWidth || 40;
39611         ts.maxTabWidth = this.config.maxTabWidth || 250;
39612         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39613         ts.monitorResize = false;
39614         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39615         ts.bodyEl.addClass('roo-layout-tabs-body');
39616         this.panels.each(this.initPanelAsTab, this);
39617     },
39618
39619     initPanelAsTab : function(panel){
39620         var ti = this.tabs.addTab(
39621             panel.getEl().id,
39622             panel.getTitle(),
39623             null,
39624             this.config.closeOnTab && panel.isClosable(),
39625             panel.tpl
39626         );
39627         if(panel.tabTip !== undefined){
39628             ti.setTooltip(panel.tabTip);
39629         }
39630         ti.on("activate", function(){
39631               this.setActivePanel(panel);
39632         }, this);
39633         
39634         if(this.config.closeOnTab){
39635             ti.on("beforeclose", function(t, e){
39636                 e.cancel = true;
39637                 this.remove(panel);
39638             }, this);
39639         }
39640         
39641         panel.tabItem = ti;
39642         
39643         return ti;
39644     },
39645
39646     updatePanelTitle : function(panel, title)
39647     {
39648         if(this.activePanel == panel){
39649             this.updateTitle(title);
39650         }
39651         if(this.tabs){
39652             var ti = this.tabs.getTab(panel.getEl().id);
39653             ti.setText(title);
39654             if(panel.tabTip !== undefined){
39655                 ti.setTooltip(panel.tabTip);
39656             }
39657         }
39658     },
39659
39660     updateTitle : function(title){
39661         if(this.titleTextEl && !this.config.title){
39662             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39663         }
39664     },
39665
39666     setActivePanel : function(panel)
39667     {
39668         panel = this.getPanel(panel);
39669         if(this.activePanel && this.activePanel != panel){
39670             if(this.activePanel.setActiveState(false) === false){
39671                 return;
39672             }
39673         }
39674         this.activePanel = panel;
39675         panel.setActiveState(true);
39676         if(this.panelSize){
39677             panel.setSize(this.panelSize.width, this.panelSize.height);
39678         }
39679         if(this.closeBtn){
39680             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39681         }
39682         this.updateTitle(panel.getTitle());
39683         if(this.tabs){
39684             this.fireEvent("invalidated", this);
39685         }
39686         this.fireEvent("panelactivated", this, panel);
39687     },
39688
39689     /**
39690      * Shows the specified panel.
39691      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39692      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39693      */
39694     showPanel : function(panel)
39695     {
39696         panel = this.getPanel(panel);
39697         if(panel){
39698             if(this.tabs){
39699                 var tab = this.tabs.getTab(panel.getEl().id);
39700                 if(tab.isHidden()){
39701                     this.tabs.unhideTab(tab.id);
39702                 }
39703                 tab.activate();
39704             }else{
39705                 this.setActivePanel(panel);
39706             }
39707         }
39708         return panel;
39709     },
39710
39711     /**
39712      * Get the active panel for this region.
39713      * @return {Roo.ContentPanel} The active panel or null
39714      */
39715     getActivePanel : function(){
39716         return this.activePanel;
39717     },
39718
39719     validateVisibility : function(){
39720         if(this.panels.getCount() < 1){
39721             this.updateTitle("&#160;");
39722             this.closeBtn.hide();
39723             this.hide();
39724         }else{
39725             if(!this.isVisible()){
39726                 this.show();
39727             }
39728         }
39729     },
39730
39731     /**
39732      * Adds the passed ContentPanel(s) to this region.
39733      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39734      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39735      */
39736     add : function(panel)
39737     {
39738         if(arguments.length > 1){
39739             for(var i = 0, len = arguments.length; i < len; i++) {
39740                 this.add(arguments[i]);
39741             }
39742             return null;
39743         }
39744         
39745         // if we have not been rendered yet, then we can not really do much of this..
39746         if (!this.bodyEl) {
39747             this.unrendered_panels.push(panel);
39748             return panel;
39749         }
39750         
39751         
39752         
39753         
39754         if(this.hasPanel(panel)){
39755             this.showPanel(panel);
39756             return panel;
39757         }
39758         panel.setRegion(this);
39759         this.panels.add(panel);
39760        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39761             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39762             // and hide them... ???
39763             this.bodyEl.dom.appendChild(panel.getEl().dom);
39764             if(panel.background !== true){
39765                 this.setActivePanel(panel);
39766             }
39767             this.fireEvent("paneladded", this, panel);
39768             return panel;
39769         }
39770         */
39771         if(!this.tabs){
39772             this.initTabs();
39773         }else{
39774             this.initPanelAsTab(panel);
39775         }
39776         
39777         
39778         if(panel.background !== true){
39779             this.tabs.activate(panel.getEl().id);
39780         }
39781         this.fireEvent("paneladded", this, panel);
39782         return panel;
39783     },
39784
39785     /**
39786      * Hides the tab for the specified panel.
39787      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39788      */
39789     hidePanel : function(panel){
39790         if(this.tabs && (panel = this.getPanel(panel))){
39791             this.tabs.hideTab(panel.getEl().id);
39792         }
39793     },
39794
39795     /**
39796      * Unhides the tab for a previously hidden panel.
39797      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39798      */
39799     unhidePanel : function(panel){
39800         if(this.tabs && (panel = this.getPanel(panel))){
39801             this.tabs.unhideTab(panel.getEl().id);
39802         }
39803     },
39804
39805     clearPanels : function(){
39806         while(this.panels.getCount() > 0){
39807              this.remove(this.panels.first());
39808         }
39809     },
39810
39811     /**
39812      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39813      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39814      * @param {Boolean} preservePanel Overrides the config preservePanel option
39815      * @return {Roo.ContentPanel} The panel that was removed
39816      */
39817     remove : function(panel, preservePanel)
39818     {
39819         panel = this.getPanel(panel);
39820         if(!panel){
39821             return null;
39822         }
39823         var e = {};
39824         this.fireEvent("beforeremove", this, panel, e);
39825         if(e.cancel === true){
39826             return null;
39827         }
39828         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39829         var panelId = panel.getId();
39830         this.panels.removeKey(panelId);
39831         if(preservePanel){
39832             document.body.appendChild(panel.getEl().dom);
39833         }
39834         if(this.tabs){
39835             this.tabs.removeTab(panel.getEl().id);
39836         }else if (!preservePanel){
39837             this.bodyEl.dom.removeChild(panel.getEl().dom);
39838         }
39839         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39840             var p = this.panels.first();
39841             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39842             tempEl.appendChild(p.getEl().dom);
39843             this.bodyEl.update("");
39844             this.bodyEl.dom.appendChild(p.getEl().dom);
39845             tempEl = null;
39846             this.updateTitle(p.getTitle());
39847             this.tabs = null;
39848             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39849             this.setActivePanel(p);
39850         }
39851         panel.setRegion(null);
39852         if(this.activePanel == panel){
39853             this.activePanel = null;
39854         }
39855         if(this.config.autoDestroy !== false && preservePanel !== true){
39856             try{panel.destroy();}catch(e){}
39857         }
39858         this.fireEvent("panelremoved", this, panel);
39859         return panel;
39860     },
39861
39862     /**
39863      * Returns the TabPanel component used by this region
39864      * @return {Roo.TabPanel}
39865      */
39866     getTabs : function(){
39867         return this.tabs;
39868     },
39869
39870     createTool : function(parentEl, className){
39871         var btn = Roo.DomHelper.append(parentEl, {
39872             tag: "div",
39873             cls: "x-layout-tools-button",
39874             children: [ {
39875                 tag: "div",
39876                 cls: "roo-layout-tools-button-inner " + className,
39877                 html: "&#160;"
39878             }]
39879         }, true);
39880         btn.addClassOnOver("roo-layout-tools-button-over");
39881         return btn;
39882     }
39883 });/*
39884  * Based on:
39885  * Ext JS Library 1.1.1
39886  * Copyright(c) 2006-2007, Ext JS, LLC.
39887  *
39888  * Originally Released Under LGPL - original licence link has changed is not relivant.
39889  *
39890  * Fork - LGPL
39891  * <script type="text/javascript">
39892  */
39893  
39894
39895
39896 /**
39897  * @class Roo.SplitLayoutRegion
39898  * @extends Roo.LayoutRegion
39899  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39900  */
39901 Roo.bootstrap.layout.Split = function(config){
39902     this.cursor = config.cursor;
39903     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39904 };
39905
39906 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39907 {
39908     splitTip : "Drag to resize.",
39909     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39910     useSplitTips : false,
39911
39912     applyConfig : function(config){
39913         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39914     },
39915     
39916     onRender : function(ctr,pos) {
39917         
39918         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39919         if(!this.config.split){
39920             return;
39921         }
39922         if(!this.split){
39923             
39924             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39925                             tag: "div",
39926                             id: this.el.id + "-split",
39927                             cls: "roo-layout-split roo-layout-split-"+this.position,
39928                             html: "&#160;"
39929             });
39930             /** The SplitBar for this region 
39931             * @type Roo.SplitBar */
39932             // does not exist yet...
39933             Roo.log([this.position, this.orientation]);
39934             
39935             this.split = new Roo.bootstrap.SplitBar({
39936                 dragElement : splitEl,
39937                 resizingElement: this.el,
39938                 orientation : this.orientation
39939             });
39940             
39941             this.split.on("moved", this.onSplitMove, this);
39942             this.split.useShim = this.config.useShim === true;
39943             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39944             if(this.useSplitTips){
39945                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39946             }
39947             //if(config.collapsible){
39948             //    this.split.el.on("dblclick", this.collapse,  this);
39949             //}
39950         }
39951         if(typeof this.config.minSize != "undefined"){
39952             this.split.minSize = this.config.minSize;
39953         }
39954         if(typeof this.config.maxSize != "undefined"){
39955             this.split.maxSize = this.config.maxSize;
39956         }
39957         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39958             this.hideSplitter();
39959         }
39960         
39961     },
39962
39963     getHMaxSize : function(){
39964          var cmax = this.config.maxSize || 10000;
39965          var center = this.mgr.getRegion("center");
39966          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39967     },
39968
39969     getVMaxSize : function(){
39970          var cmax = this.config.maxSize || 10000;
39971          var center = this.mgr.getRegion("center");
39972          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39973     },
39974
39975     onSplitMove : function(split, newSize){
39976         this.fireEvent("resized", this, newSize);
39977     },
39978     
39979     /** 
39980      * Returns the {@link Roo.SplitBar} for this region.
39981      * @return {Roo.SplitBar}
39982      */
39983     getSplitBar : function(){
39984         return this.split;
39985     },
39986     
39987     hide : function(){
39988         this.hideSplitter();
39989         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39990     },
39991
39992     hideSplitter : function(){
39993         if(this.split){
39994             this.split.el.setLocation(-2000,-2000);
39995             this.split.el.hide();
39996         }
39997     },
39998
39999     show : function(){
40000         if(this.split){
40001             this.split.el.show();
40002         }
40003         Roo.bootstrap.layout.Split.superclass.show.call(this);
40004     },
40005     
40006     beforeSlide: function(){
40007         if(Roo.isGecko){// firefox overflow auto bug workaround
40008             this.bodyEl.clip();
40009             if(this.tabs) {
40010                 this.tabs.bodyEl.clip();
40011             }
40012             if(this.activePanel){
40013                 this.activePanel.getEl().clip();
40014                 
40015                 if(this.activePanel.beforeSlide){
40016                     this.activePanel.beforeSlide();
40017                 }
40018             }
40019         }
40020     },
40021     
40022     afterSlide : function(){
40023         if(Roo.isGecko){// firefox overflow auto bug workaround
40024             this.bodyEl.unclip();
40025             if(this.tabs) {
40026                 this.tabs.bodyEl.unclip();
40027             }
40028             if(this.activePanel){
40029                 this.activePanel.getEl().unclip();
40030                 if(this.activePanel.afterSlide){
40031                     this.activePanel.afterSlide();
40032                 }
40033             }
40034         }
40035     },
40036
40037     initAutoHide : function(){
40038         if(this.autoHide !== false){
40039             if(!this.autoHideHd){
40040                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40041                 this.autoHideHd = {
40042                     "mouseout": function(e){
40043                         if(!e.within(this.el, true)){
40044                             st.delay(500);
40045                         }
40046                     },
40047                     "mouseover" : function(e){
40048                         st.cancel();
40049                     },
40050                     scope : this
40051                 };
40052             }
40053             this.el.on(this.autoHideHd);
40054         }
40055     },
40056
40057     clearAutoHide : function(){
40058         if(this.autoHide !== false){
40059             this.el.un("mouseout", this.autoHideHd.mouseout);
40060             this.el.un("mouseover", this.autoHideHd.mouseover);
40061         }
40062     },
40063
40064     clearMonitor : function(){
40065         Roo.get(document).un("click", this.slideInIf, this);
40066     },
40067
40068     // these names are backwards but not changed for compat
40069     slideOut : function(){
40070         if(this.isSlid || this.el.hasActiveFx()){
40071             return;
40072         }
40073         this.isSlid = true;
40074         if(this.collapseBtn){
40075             this.collapseBtn.hide();
40076         }
40077         this.closeBtnState = this.closeBtn.getStyle('display');
40078         this.closeBtn.hide();
40079         if(this.stickBtn){
40080             this.stickBtn.show();
40081         }
40082         this.el.show();
40083         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40084         this.beforeSlide();
40085         this.el.setStyle("z-index", 10001);
40086         this.el.slideIn(this.getSlideAnchor(), {
40087             callback: function(){
40088                 this.afterSlide();
40089                 this.initAutoHide();
40090                 Roo.get(document).on("click", this.slideInIf, this);
40091                 this.fireEvent("slideshow", this);
40092             },
40093             scope: this,
40094             block: true
40095         });
40096     },
40097
40098     afterSlideIn : function(){
40099         this.clearAutoHide();
40100         this.isSlid = false;
40101         this.clearMonitor();
40102         this.el.setStyle("z-index", "");
40103         if(this.collapseBtn){
40104             this.collapseBtn.show();
40105         }
40106         this.closeBtn.setStyle('display', this.closeBtnState);
40107         if(this.stickBtn){
40108             this.stickBtn.hide();
40109         }
40110         this.fireEvent("slidehide", this);
40111     },
40112
40113     slideIn : function(cb){
40114         if(!this.isSlid || this.el.hasActiveFx()){
40115             Roo.callback(cb);
40116             return;
40117         }
40118         this.isSlid = false;
40119         this.beforeSlide();
40120         this.el.slideOut(this.getSlideAnchor(), {
40121             callback: function(){
40122                 this.el.setLeftTop(-10000, -10000);
40123                 this.afterSlide();
40124                 this.afterSlideIn();
40125                 Roo.callback(cb);
40126             },
40127             scope: this,
40128             block: true
40129         });
40130     },
40131     
40132     slideInIf : function(e){
40133         if(!e.within(this.el)){
40134             this.slideIn();
40135         }
40136     },
40137
40138     animateCollapse : function(){
40139         this.beforeSlide();
40140         this.el.setStyle("z-index", 20000);
40141         var anchor = this.getSlideAnchor();
40142         this.el.slideOut(anchor, {
40143             callback : function(){
40144                 this.el.setStyle("z-index", "");
40145                 this.collapsedEl.slideIn(anchor, {duration:.3});
40146                 this.afterSlide();
40147                 this.el.setLocation(-10000,-10000);
40148                 this.el.hide();
40149                 this.fireEvent("collapsed", this);
40150             },
40151             scope: this,
40152             block: true
40153         });
40154     },
40155
40156     animateExpand : function(){
40157         this.beforeSlide();
40158         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40159         this.el.setStyle("z-index", 20000);
40160         this.collapsedEl.hide({
40161             duration:.1
40162         });
40163         this.el.slideIn(this.getSlideAnchor(), {
40164             callback : function(){
40165                 this.el.setStyle("z-index", "");
40166                 this.afterSlide();
40167                 if(this.split){
40168                     this.split.el.show();
40169                 }
40170                 this.fireEvent("invalidated", this);
40171                 this.fireEvent("expanded", this);
40172             },
40173             scope: this,
40174             block: true
40175         });
40176     },
40177
40178     anchors : {
40179         "west" : "left",
40180         "east" : "right",
40181         "north" : "top",
40182         "south" : "bottom"
40183     },
40184
40185     sanchors : {
40186         "west" : "l",
40187         "east" : "r",
40188         "north" : "t",
40189         "south" : "b"
40190     },
40191
40192     canchors : {
40193         "west" : "tl-tr",
40194         "east" : "tr-tl",
40195         "north" : "tl-bl",
40196         "south" : "bl-tl"
40197     },
40198
40199     getAnchor : function(){
40200         return this.anchors[this.position];
40201     },
40202
40203     getCollapseAnchor : function(){
40204         return this.canchors[this.position];
40205     },
40206
40207     getSlideAnchor : function(){
40208         return this.sanchors[this.position];
40209     },
40210
40211     getAlignAdj : function(){
40212         var cm = this.cmargins;
40213         switch(this.position){
40214             case "west":
40215                 return [0, 0];
40216             break;
40217             case "east":
40218                 return [0, 0];
40219             break;
40220             case "north":
40221                 return [0, 0];
40222             break;
40223             case "south":
40224                 return [0, 0];
40225             break;
40226         }
40227     },
40228
40229     getExpandAdj : function(){
40230         var c = this.collapsedEl, cm = this.cmargins;
40231         switch(this.position){
40232             case "west":
40233                 return [-(cm.right+c.getWidth()+cm.left), 0];
40234             break;
40235             case "east":
40236                 return [cm.right+c.getWidth()+cm.left, 0];
40237             break;
40238             case "north":
40239                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40240             break;
40241             case "south":
40242                 return [0, cm.top+cm.bottom+c.getHeight()];
40243             break;
40244         }
40245     }
40246 });/*
40247  * Based on:
40248  * Ext JS Library 1.1.1
40249  * Copyright(c) 2006-2007, Ext JS, LLC.
40250  *
40251  * Originally Released Under LGPL - original licence link has changed is not relivant.
40252  *
40253  * Fork - LGPL
40254  * <script type="text/javascript">
40255  */
40256 /*
40257  * These classes are private internal classes
40258  */
40259 Roo.bootstrap.layout.Center = function(config){
40260     config.region = "center";
40261     Roo.bootstrap.layout.Region.call(this, config);
40262     this.visible = true;
40263     this.minWidth = config.minWidth || 20;
40264     this.minHeight = config.minHeight || 20;
40265 };
40266
40267 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40268     hide : function(){
40269         // center panel can't be hidden
40270     },
40271     
40272     show : function(){
40273         // center panel can't be hidden
40274     },
40275     
40276     getMinWidth: function(){
40277         return this.minWidth;
40278     },
40279     
40280     getMinHeight: function(){
40281         return this.minHeight;
40282     }
40283 });
40284
40285
40286
40287
40288  
40289
40290
40291
40292
40293
40294
40295 Roo.bootstrap.layout.North = function(config)
40296 {
40297     config.region = 'north';
40298     config.cursor = 'n-resize';
40299     
40300     Roo.bootstrap.layout.Split.call(this, config);
40301     
40302     
40303     if(this.split){
40304         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40305         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40306         this.split.el.addClass("roo-layout-split-v");
40307     }
40308     //var size = config.initialSize || config.height;
40309     //if(this.el && typeof size != "undefined"){
40310     //    this.el.setHeight(size);
40311     //}
40312 };
40313 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40314 {
40315     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40316      
40317      
40318     onRender : function(ctr, pos)
40319     {
40320         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40321         var size = this.config.initialSize || this.config.height;
40322         if(this.el && typeof size != "undefined"){
40323             this.el.setHeight(size);
40324         }
40325     
40326     },
40327     
40328     getBox : function(){
40329         if(this.collapsed){
40330             return this.collapsedEl.getBox();
40331         }
40332         var box = this.el.getBox();
40333         if(this.split){
40334             box.height += this.split.el.getHeight();
40335         }
40336         return box;
40337     },
40338     
40339     updateBox : function(box){
40340         if(this.split && !this.collapsed){
40341             box.height -= this.split.el.getHeight();
40342             this.split.el.setLeft(box.x);
40343             this.split.el.setTop(box.y+box.height);
40344             this.split.el.setWidth(box.width);
40345         }
40346         if(this.collapsed){
40347             this.updateBody(box.width, null);
40348         }
40349         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40350     }
40351 });
40352
40353
40354
40355
40356
40357 Roo.bootstrap.layout.South = function(config){
40358     config.region = 'south';
40359     config.cursor = 's-resize';
40360     Roo.bootstrap.layout.Split.call(this, config);
40361     if(this.split){
40362         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40363         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40364         this.split.el.addClass("roo-layout-split-v");
40365     }
40366     
40367 };
40368
40369 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40370     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40371     
40372     onRender : function(ctr, pos)
40373     {
40374         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40375         var size = this.config.initialSize || this.config.height;
40376         if(this.el && typeof size != "undefined"){
40377             this.el.setHeight(size);
40378         }
40379     
40380     },
40381     
40382     getBox : function(){
40383         if(this.collapsed){
40384             return this.collapsedEl.getBox();
40385         }
40386         var box = this.el.getBox();
40387         if(this.split){
40388             var sh = this.split.el.getHeight();
40389             box.height += sh;
40390             box.y -= sh;
40391         }
40392         return box;
40393     },
40394     
40395     updateBox : function(box){
40396         if(this.split && !this.collapsed){
40397             var sh = this.split.el.getHeight();
40398             box.height -= sh;
40399             box.y += sh;
40400             this.split.el.setLeft(box.x);
40401             this.split.el.setTop(box.y-sh);
40402             this.split.el.setWidth(box.width);
40403         }
40404         if(this.collapsed){
40405             this.updateBody(box.width, null);
40406         }
40407         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40408     }
40409 });
40410
40411 Roo.bootstrap.layout.East = function(config){
40412     config.region = "east";
40413     config.cursor = "e-resize";
40414     Roo.bootstrap.layout.Split.call(this, config);
40415     if(this.split){
40416         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40417         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40418         this.split.el.addClass("roo-layout-split-h");
40419     }
40420     
40421 };
40422 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40423     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40424     
40425     onRender : function(ctr, pos)
40426     {
40427         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40428         var size = this.config.initialSize || this.config.width;
40429         if(this.el && typeof size != "undefined"){
40430             this.el.setWidth(size);
40431         }
40432     
40433     },
40434     
40435     getBox : function(){
40436         if(this.collapsed){
40437             return this.collapsedEl.getBox();
40438         }
40439         var box = this.el.getBox();
40440         if(this.split){
40441             var sw = this.split.el.getWidth();
40442             box.width += sw;
40443             box.x -= sw;
40444         }
40445         return box;
40446     },
40447
40448     updateBox : function(box){
40449         if(this.split && !this.collapsed){
40450             var sw = this.split.el.getWidth();
40451             box.width -= sw;
40452             this.split.el.setLeft(box.x);
40453             this.split.el.setTop(box.y);
40454             this.split.el.setHeight(box.height);
40455             box.x += sw;
40456         }
40457         if(this.collapsed){
40458             this.updateBody(null, box.height);
40459         }
40460         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40461     }
40462 });
40463
40464 Roo.bootstrap.layout.West = function(config){
40465     config.region = "west";
40466     config.cursor = "w-resize";
40467     
40468     Roo.bootstrap.layout.Split.call(this, config);
40469     if(this.split){
40470         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40471         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40472         this.split.el.addClass("roo-layout-split-h");
40473     }
40474     
40475 };
40476 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40477     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40478     
40479     onRender: function(ctr, pos)
40480     {
40481         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40482         var size = this.config.initialSize || this.config.width;
40483         if(typeof size != "undefined"){
40484             this.el.setWidth(size);
40485         }
40486     },
40487     
40488     getBox : function(){
40489         if(this.collapsed){
40490             return this.collapsedEl.getBox();
40491         }
40492         var box = this.el.getBox();
40493         if (box.width == 0) {
40494             box.width = this.config.width; // kludge?
40495         }
40496         if(this.split){
40497             box.width += this.split.el.getWidth();
40498         }
40499         return box;
40500     },
40501     
40502     updateBox : function(box){
40503         if(this.split && !this.collapsed){
40504             var sw = this.split.el.getWidth();
40505             box.width -= sw;
40506             this.split.el.setLeft(box.x+box.width);
40507             this.split.el.setTop(box.y);
40508             this.split.el.setHeight(box.height);
40509         }
40510         if(this.collapsed){
40511             this.updateBody(null, box.height);
40512         }
40513         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40514     }
40515 });Roo.namespace("Roo.bootstrap.panel");/*
40516  * Based on:
40517  * Ext JS Library 1.1.1
40518  * Copyright(c) 2006-2007, Ext JS, LLC.
40519  *
40520  * Originally Released Under LGPL - original licence link has changed is not relivant.
40521  *
40522  * Fork - LGPL
40523  * <script type="text/javascript">
40524  */
40525 /**
40526  * @class Roo.ContentPanel
40527  * @extends Roo.util.Observable
40528  * A basic ContentPanel element.
40529  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40530  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40531  * @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
40532  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40533  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40534  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40535  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40536  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40537  * @cfg {String} title          The title for this panel
40538  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40539  * @cfg {String} url            Calls {@link #setUrl} with this value
40540  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40541  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40542  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40543  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40544  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40545  * @cfg {Boolean} badges render the badges
40546  * @cfg {String} cls  extra classes to use  
40547  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40548
40549  * @constructor
40550  * Create a new ContentPanel.
40551  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40552  * @param {String/Object} config A string to set only the title or a config object
40553  * @param {String} content (optional) Set the HTML content for this panel
40554  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40555  */
40556 Roo.bootstrap.panel.Content = function( config){
40557     
40558     this.tpl = config.tpl || false;
40559     
40560     var el = config.el;
40561     var content = config.content;
40562
40563     if(config.autoCreate){ // xtype is available if this is called from factory
40564         el = Roo.id();
40565     }
40566     this.el = Roo.get(el);
40567     if(!this.el && config && config.autoCreate){
40568         if(typeof config.autoCreate == "object"){
40569             if(!config.autoCreate.id){
40570                 config.autoCreate.id = config.id||el;
40571             }
40572             this.el = Roo.DomHelper.append(document.body,
40573                         config.autoCreate, true);
40574         }else{
40575             var elcfg =  {
40576                 tag: "div",
40577                 cls: (config.cls || '') +
40578                     (config.background ? ' bg-' + config.background : '') +
40579                     " roo-layout-inactive-content",
40580                 id: config.id||el
40581             };
40582             if (config.iframe) {
40583                 elcfg.cn = [
40584                     {
40585                         tag : 'iframe',
40586                         style : 'border: 0px',
40587                         src : 'about:blank'
40588                     }
40589                 ];
40590             }
40591               
40592             if (config.html) {
40593                 elcfg.html = config.html;
40594                 
40595             }
40596                         
40597             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40598             if (config.iframe) {
40599                 this.iframeEl = this.el.select('iframe',true).first();
40600             }
40601             
40602         }
40603     } 
40604     this.closable = false;
40605     this.loaded = false;
40606     this.active = false;
40607    
40608       
40609     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40610         
40611         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40612         
40613         this.wrapEl = this.el; //this.el.wrap();
40614         var ti = [];
40615         if (config.toolbar.items) {
40616             ti = config.toolbar.items ;
40617             delete config.toolbar.items ;
40618         }
40619         
40620         var nitems = [];
40621         this.toolbar.render(this.wrapEl, 'before');
40622         for(var i =0;i < ti.length;i++) {
40623           //  Roo.log(['add child', items[i]]);
40624             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40625         }
40626         this.toolbar.items = nitems;
40627         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40628         delete config.toolbar;
40629         
40630     }
40631     /*
40632     // xtype created footer. - not sure if will work as we normally have to render first..
40633     if (this.footer && !this.footer.el && this.footer.xtype) {
40634         if (!this.wrapEl) {
40635             this.wrapEl = this.el.wrap();
40636         }
40637     
40638         this.footer.container = this.wrapEl.createChild();
40639          
40640         this.footer = Roo.factory(this.footer, Roo);
40641         
40642     }
40643     */
40644     
40645      if(typeof config == "string"){
40646         this.title = config;
40647     }else{
40648         Roo.apply(this, config);
40649     }
40650     
40651     if(this.resizeEl){
40652         this.resizeEl = Roo.get(this.resizeEl, true);
40653     }else{
40654         this.resizeEl = this.el;
40655     }
40656     // handle view.xtype
40657     
40658  
40659     
40660     
40661     this.addEvents({
40662         /**
40663          * @event activate
40664          * Fires when this panel is activated. 
40665          * @param {Roo.ContentPanel} this
40666          */
40667         "activate" : true,
40668         /**
40669          * @event deactivate
40670          * Fires when this panel is activated. 
40671          * @param {Roo.ContentPanel} this
40672          */
40673         "deactivate" : true,
40674
40675         /**
40676          * @event resize
40677          * Fires when this panel is resized if fitToFrame is true.
40678          * @param {Roo.ContentPanel} this
40679          * @param {Number} width The width after any component adjustments
40680          * @param {Number} height The height after any component adjustments
40681          */
40682         "resize" : true,
40683         
40684          /**
40685          * @event render
40686          * Fires when this tab is created
40687          * @param {Roo.ContentPanel} this
40688          */
40689         "render" : true,
40690         
40691           /**
40692          * @event scroll
40693          * Fires when this content is scrolled
40694          * @param {Roo.ContentPanel} this
40695          * @param {Event} scrollEvent
40696          */
40697         "scroll" : true
40698         
40699         
40700         
40701     });
40702     
40703
40704     
40705     
40706     if(this.autoScroll && !this.iframe){
40707         this.resizeEl.setStyle("overflow", "auto");
40708         this.resizeEl.on('scroll', this.onScroll, this);
40709     } else {
40710         // fix randome scrolling
40711         //this.el.on('scroll', function() {
40712         //    Roo.log('fix random scolling');
40713         //    this.scrollTo('top',0); 
40714         //});
40715     }
40716     content = content || this.content;
40717     if(content){
40718         this.setContent(content);
40719     }
40720     if(config && config.url){
40721         this.setUrl(this.url, this.params, this.loadOnce);
40722     }
40723     
40724     
40725     
40726     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40727     
40728     if (this.view && typeof(this.view.xtype) != 'undefined') {
40729         this.view.el = this.el.appendChild(document.createElement("div"));
40730         this.view = Roo.factory(this.view); 
40731         this.view.render  &&  this.view.render(false, '');  
40732     }
40733     
40734     
40735     this.fireEvent('render', this);
40736 };
40737
40738 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40739     
40740     cls : '',
40741     background : '',
40742     
40743     tabTip : '',
40744     
40745     iframe : false,
40746     iframeEl : false,
40747     
40748     /* Resize Element - use this to work out scroll etc. */
40749     resizeEl : false,
40750     
40751     setRegion : function(region){
40752         this.region = region;
40753         this.setActiveClass(region && !this.background);
40754     },
40755     
40756     
40757     setActiveClass: function(state)
40758     {
40759         if(state){
40760            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40761            this.el.setStyle('position','relative');
40762         }else{
40763            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40764            this.el.setStyle('position', 'absolute');
40765         } 
40766     },
40767     
40768     /**
40769      * Returns the toolbar for this Panel if one was configured. 
40770      * @return {Roo.Toolbar} 
40771      */
40772     getToolbar : function(){
40773         return this.toolbar;
40774     },
40775     
40776     setActiveState : function(active)
40777     {
40778         this.active = active;
40779         this.setActiveClass(active);
40780         if(!active){
40781             if(this.fireEvent("deactivate", this) === false){
40782                 return false;
40783             }
40784             return true;
40785         }
40786         this.fireEvent("activate", this);
40787         return true;
40788     },
40789     /**
40790      * Updates this panel's element (not for iframe)
40791      * @param {String} content The new content
40792      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40793     */
40794     setContent : function(content, loadScripts){
40795         if (this.iframe) {
40796             return;
40797         }
40798         
40799         this.el.update(content, loadScripts);
40800     },
40801
40802     ignoreResize : function(w, h){
40803         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40804             return true;
40805         }else{
40806             this.lastSize = {width: w, height: h};
40807             return false;
40808         }
40809     },
40810     /**
40811      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40812      * @return {Roo.UpdateManager} The UpdateManager
40813      */
40814     getUpdateManager : function(){
40815         if (this.iframe) {
40816             return false;
40817         }
40818         return this.el.getUpdateManager();
40819     },
40820      /**
40821      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40822      * Does not work with IFRAME contents
40823      * @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:
40824 <pre><code>
40825 panel.load({
40826     url: "your-url.php",
40827     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40828     callback: yourFunction,
40829     scope: yourObject, //(optional scope)
40830     discardUrl: false,
40831     nocache: false,
40832     text: "Loading...",
40833     timeout: 30,
40834     scripts: false
40835 });
40836 </code></pre>
40837      
40838      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40839      * 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.
40840      * @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}
40841      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40842      * @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.
40843      * @return {Roo.ContentPanel} this
40844      */
40845     load : function(){
40846         
40847         if (this.iframe) {
40848             return this;
40849         }
40850         
40851         var um = this.el.getUpdateManager();
40852         um.update.apply(um, arguments);
40853         return this;
40854     },
40855
40856
40857     /**
40858      * 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.
40859      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40860      * @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)
40861      * @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)
40862      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40863      */
40864     setUrl : function(url, params, loadOnce){
40865         if (this.iframe) {
40866             this.iframeEl.dom.src = url;
40867             return false;
40868         }
40869         
40870         if(this.refreshDelegate){
40871             this.removeListener("activate", this.refreshDelegate);
40872         }
40873         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40874         this.on("activate", this.refreshDelegate);
40875         return this.el.getUpdateManager();
40876     },
40877     
40878     _handleRefresh : function(url, params, loadOnce){
40879         if(!loadOnce || !this.loaded){
40880             var updater = this.el.getUpdateManager();
40881             updater.update(url, params, this._setLoaded.createDelegate(this));
40882         }
40883     },
40884     
40885     _setLoaded : function(){
40886         this.loaded = true;
40887     }, 
40888     
40889     /**
40890      * Returns this panel's id
40891      * @return {String} 
40892      */
40893     getId : function(){
40894         return this.el.id;
40895     },
40896     
40897     /** 
40898      * Returns this panel's element - used by regiosn to add.
40899      * @return {Roo.Element} 
40900      */
40901     getEl : function(){
40902         return this.wrapEl || this.el;
40903     },
40904     
40905    
40906     
40907     adjustForComponents : function(width, height)
40908     {
40909         //Roo.log('adjustForComponents ');
40910         if(this.resizeEl != this.el){
40911             width -= this.el.getFrameWidth('lr');
40912             height -= this.el.getFrameWidth('tb');
40913         }
40914         if(this.toolbar){
40915             var te = this.toolbar.getEl();
40916             te.setWidth(width);
40917             height -= te.getHeight();
40918         }
40919         if(this.footer){
40920             var te = this.footer.getEl();
40921             te.setWidth(width);
40922             height -= te.getHeight();
40923         }
40924         
40925         
40926         if(this.adjustments){
40927             width += this.adjustments[0];
40928             height += this.adjustments[1];
40929         }
40930         return {"width": width, "height": height};
40931     },
40932     
40933     setSize : function(width, height){
40934         if(this.fitToFrame && !this.ignoreResize(width, height)){
40935             if(this.fitContainer && this.resizeEl != this.el){
40936                 this.el.setSize(width, height);
40937             }
40938             var size = this.adjustForComponents(width, height);
40939             if (this.iframe) {
40940                 this.iframeEl.setSize(width,height);
40941             }
40942             
40943             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40944             this.fireEvent('resize', this, size.width, size.height);
40945             
40946             
40947         }
40948     },
40949     
40950     /**
40951      * Returns this panel's title
40952      * @return {String} 
40953      */
40954     getTitle : function(){
40955         
40956         if (typeof(this.title) != 'object') {
40957             return this.title;
40958         }
40959         
40960         var t = '';
40961         for (var k in this.title) {
40962             if (!this.title.hasOwnProperty(k)) {
40963                 continue;
40964             }
40965             
40966             if (k.indexOf('-') >= 0) {
40967                 var s = k.split('-');
40968                 for (var i = 0; i<s.length; i++) {
40969                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40970                 }
40971             } else {
40972                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40973             }
40974         }
40975         return t;
40976     },
40977     
40978     /**
40979      * Set this panel's title
40980      * @param {String} title
40981      */
40982     setTitle : function(title){
40983         this.title = title;
40984         if(this.region){
40985             this.region.updatePanelTitle(this, title);
40986         }
40987     },
40988     
40989     /**
40990      * Returns true is this panel was configured to be closable
40991      * @return {Boolean} 
40992      */
40993     isClosable : function(){
40994         return this.closable;
40995     },
40996     
40997     beforeSlide : function(){
40998         this.el.clip();
40999         this.resizeEl.clip();
41000     },
41001     
41002     afterSlide : function(){
41003         this.el.unclip();
41004         this.resizeEl.unclip();
41005     },
41006     
41007     /**
41008      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41009      *   Will fail silently if the {@link #setUrl} method has not been called.
41010      *   This does not activate the panel, just updates its content.
41011      */
41012     refresh : function(){
41013         if(this.refreshDelegate){
41014            this.loaded = false;
41015            this.refreshDelegate();
41016         }
41017     },
41018     
41019     /**
41020      * Destroys this panel
41021      */
41022     destroy : function(){
41023         this.el.removeAllListeners();
41024         var tempEl = document.createElement("span");
41025         tempEl.appendChild(this.el.dom);
41026         tempEl.innerHTML = "";
41027         this.el.remove();
41028         this.el = null;
41029     },
41030     
41031     /**
41032      * form - if the content panel contains a form - this is a reference to it.
41033      * @type {Roo.form.Form}
41034      */
41035     form : false,
41036     /**
41037      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41038      *    This contains a reference to it.
41039      * @type {Roo.View}
41040      */
41041     view : false,
41042     
41043       /**
41044      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41045      * <pre><code>
41046
41047 layout.addxtype({
41048        xtype : 'Form',
41049        items: [ .... ]
41050    }
41051 );
41052
41053 </code></pre>
41054      * @param {Object} cfg Xtype definition of item to add.
41055      */
41056     
41057     
41058     getChildContainer: function () {
41059         return this.getEl();
41060     },
41061     
41062     
41063     onScroll : function(e)
41064     {
41065         this.fireEvent('scroll', this, e);
41066     }
41067     
41068     
41069     /*
41070         var  ret = new Roo.factory(cfg);
41071         return ret;
41072         
41073         
41074         // add form..
41075         if (cfg.xtype.match(/^Form$/)) {
41076             
41077             var el;
41078             //if (this.footer) {
41079             //    el = this.footer.container.insertSibling(false, 'before');
41080             //} else {
41081                 el = this.el.createChild();
41082             //}
41083
41084             this.form = new  Roo.form.Form(cfg);
41085             
41086             
41087             if ( this.form.allItems.length) {
41088                 this.form.render(el.dom);
41089             }
41090             return this.form;
41091         }
41092         // should only have one of theses..
41093         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41094             // views.. should not be just added - used named prop 'view''
41095             
41096             cfg.el = this.el.appendChild(document.createElement("div"));
41097             // factory?
41098             
41099             var ret = new Roo.factory(cfg);
41100              
41101              ret.render && ret.render(false, ''); // render blank..
41102             this.view = ret;
41103             return ret;
41104         }
41105         return false;
41106     }
41107     \*/
41108 });
41109  
41110 /**
41111  * @class Roo.bootstrap.panel.Grid
41112  * @extends Roo.bootstrap.panel.Content
41113  * @constructor
41114  * Create a new GridPanel.
41115  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41116  * @param {Object} config A the config object
41117   
41118  */
41119
41120
41121
41122 Roo.bootstrap.panel.Grid = function(config)
41123 {
41124     
41125       
41126     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41127         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41128
41129     config.el = this.wrapper;
41130     //this.el = this.wrapper;
41131     
41132       if (config.container) {
41133         // ctor'ed from a Border/panel.grid
41134         
41135         
41136         this.wrapper.setStyle("overflow", "hidden");
41137         this.wrapper.addClass('roo-grid-container');
41138
41139     }
41140     
41141     
41142     if(config.toolbar){
41143         var tool_el = this.wrapper.createChild();    
41144         this.toolbar = Roo.factory(config.toolbar);
41145         var ti = [];
41146         if (config.toolbar.items) {
41147             ti = config.toolbar.items ;
41148             delete config.toolbar.items ;
41149         }
41150         
41151         var nitems = [];
41152         this.toolbar.render(tool_el);
41153         for(var i =0;i < ti.length;i++) {
41154           //  Roo.log(['add child', items[i]]);
41155             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41156         }
41157         this.toolbar.items = nitems;
41158         
41159         delete config.toolbar;
41160     }
41161     
41162     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41163     config.grid.scrollBody = true;;
41164     config.grid.monitorWindowResize = false; // turn off autosizing
41165     config.grid.autoHeight = false;
41166     config.grid.autoWidth = false;
41167     
41168     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41169     
41170     if (config.background) {
41171         // render grid on panel activation (if panel background)
41172         this.on('activate', function(gp) {
41173             if (!gp.grid.rendered) {
41174                 gp.grid.render(this.wrapper);
41175                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41176             }
41177         });
41178             
41179     } else {
41180         this.grid.render(this.wrapper);
41181         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41182
41183     }
41184     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41185     // ??? needed ??? config.el = this.wrapper;
41186     
41187     
41188     
41189   
41190     // xtype created footer. - not sure if will work as we normally have to render first..
41191     if (this.footer && !this.footer.el && this.footer.xtype) {
41192         
41193         var ctr = this.grid.getView().getFooterPanel(true);
41194         this.footer.dataSource = this.grid.dataSource;
41195         this.footer = Roo.factory(this.footer, Roo);
41196         this.footer.render(ctr);
41197         
41198     }
41199     
41200     
41201     
41202     
41203      
41204 };
41205
41206 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41207     getId : function(){
41208         return this.grid.id;
41209     },
41210     
41211     /**
41212      * Returns the grid for this panel
41213      * @return {Roo.bootstrap.Table} 
41214      */
41215     getGrid : function(){
41216         return this.grid;    
41217     },
41218     
41219     setSize : function(width, height){
41220         if(!this.ignoreResize(width, height)){
41221             var grid = this.grid;
41222             var size = this.adjustForComponents(width, height);
41223             // tfoot is not a footer?
41224           
41225             
41226             var gridel = grid.getGridEl();
41227             gridel.setSize(size.width, size.height);
41228             
41229             var tbd = grid.getGridEl().select('tbody', true).first();
41230             var thd = grid.getGridEl().select('thead',true).first();
41231             var tbf= grid.getGridEl().select('tfoot', true).first();
41232
41233             if (tbf) {
41234                 size.height -= tbf.getHeight();
41235             }
41236             if (thd) {
41237                 size.height -= thd.getHeight();
41238             }
41239             
41240             tbd.setSize(size.width, size.height );
41241             // this is for the account management tab -seems to work there.
41242             var thd = grid.getGridEl().select('thead',true).first();
41243             //if (tbd) {
41244             //    tbd.setSize(size.width, size.height - thd.getHeight());
41245             //}
41246              
41247             grid.autoSize();
41248         }
41249     },
41250      
41251     
41252     
41253     beforeSlide : function(){
41254         this.grid.getView().scroller.clip();
41255     },
41256     
41257     afterSlide : function(){
41258         this.grid.getView().scroller.unclip();
41259     },
41260     
41261     destroy : function(){
41262         this.grid.destroy();
41263         delete this.grid;
41264         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41265     }
41266 });
41267
41268 /**
41269  * @class Roo.bootstrap.panel.Nest
41270  * @extends Roo.bootstrap.panel.Content
41271  * @constructor
41272  * Create a new Panel, that can contain a layout.Border.
41273  * 
41274  * 
41275  * @param {Roo.BorderLayout} layout The layout for this panel
41276  * @param {String/Object} config A string to set only the title or a config object
41277  */
41278 Roo.bootstrap.panel.Nest = function(config)
41279 {
41280     // construct with only one argument..
41281     /* FIXME - implement nicer consturctors
41282     if (layout.layout) {
41283         config = layout;
41284         layout = config.layout;
41285         delete config.layout;
41286     }
41287     if (layout.xtype && !layout.getEl) {
41288         // then layout needs constructing..
41289         layout = Roo.factory(layout, Roo);
41290     }
41291     */
41292     
41293     config.el =  config.layout.getEl();
41294     
41295     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41296     
41297     config.layout.monitorWindowResize = false; // turn off autosizing
41298     this.layout = config.layout;
41299     this.layout.getEl().addClass("roo-layout-nested-layout");
41300     this.layout.parent = this;
41301     
41302     
41303     
41304     
41305 };
41306
41307 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41308
41309     setSize : function(width, height){
41310         if(!this.ignoreResize(width, height)){
41311             var size = this.adjustForComponents(width, height);
41312             var el = this.layout.getEl();
41313             if (size.height < 1) {
41314                 el.setWidth(size.width);   
41315             } else {
41316                 el.setSize(size.width, size.height);
41317             }
41318             var touch = el.dom.offsetWidth;
41319             this.layout.layout();
41320             // ie requires a double layout on the first pass
41321             if(Roo.isIE && !this.initialized){
41322                 this.initialized = true;
41323                 this.layout.layout();
41324             }
41325         }
41326     },
41327     
41328     // activate all subpanels if not currently active..
41329     
41330     setActiveState : function(active){
41331         this.active = active;
41332         this.setActiveClass(active);
41333         
41334         if(!active){
41335             this.fireEvent("deactivate", this);
41336             return;
41337         }
41338         
41339         this.fireEvent("activate", this);
41340         // not sure if this should happen before or after..
41341         if (!this.layout) {
41342             return; // should not happen..
41343         }
41344         var reg = false;
41345         for (var r in this.layout.regions) {
41346             reg = this.layout.getRegion(r);
41347             if (reg.getActivePanel()) {
41348                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41349                 reg.setActivePanel(reg.getActivePanel());
41350                 continue;
41351             }
41352             if (!reg.panels.length) {
41353                 continue;
41354             }
41355             reg.showPanel(reg.getPanel(0));
41356         }
41357         
41358         
41359         
41360         
41361     },
41362     
41363     /**
41364      * Returns the nested BorderLayout for this panel
41365      * @return {Roo.BorderLayout} 
41366      */
41367     getLayout : function(){
41368         return this.layout;
41369     },
41370     
41371      /**
41372      * Adds a xtype elements to the layout of the nested panel
41373      * <pre><code>
41374
41375 panel.addxtype({
41376        xtype : 'ContentPanel',
41377        region: 'west',
41378        items: [ .... ]
41379    }
41380 );
41381
41382 panel.addxtype({
41383         xtype : 'NestedLayoutPanel',
41384         region: 'west',
41385         layout: {
41386            center: { },
41387            west: { }   
41388         },
41389         items : [ ... list of content panels or nested layout panels.. ]
41390    }
41391 );
41392 </code></pre>
41393      * @param {Object} cfg Xtype definition of item to add.
41394      */
41395     addxtype : function(cfg) {
41396         return this.layout.addxtype(cfg);
41397     
41398     }
41399 });/*
41400  * Based on:
41401  * Ext JS Library 1.1.1
41402  * Copyright(c) 2006-2007, Ext JS, LLC.
41403  *
41404  * Originally Released Under LGPL - original licence link has changed is not relivant.
41405  *
41406  * Fork - LGPL
41407  * <script type="text/javascript">
41408  */
41409 /**
41410  * @class Roo.TabPanel
41411  * @extends Roo.util.Observable
41412  * A lightweight tab container.
41413  * <br><br>
41414  * Usage:
41415  * <pre><code>
41416 // basic tabs 1, built from existing content
41417 var tabs = new Roo.TabPanel("tabs1");
41418 tabs.addTab("script", "View Script");
41419 tabs.addTab("markup", "View Markup");
41420 tabs.activate("script");
41421
41422 // more advanced tabs, built from javascript
41423 var jtabs = new Roo.TabPanel("jtabs");
41424 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41425
41426 // set up the UpdateManager
41427 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41428 var updater = tab2.getUpdateManager();
41429 updater.setDefaultUrl("ajax1.htm");
41430 tab2.on('activate', updater.refresh, updater, true);
41431
41432 // Use setUrl for Ajax loading
41433 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41434 tab3.setUrl("ajax2.htm", null, true);
41435
41436 // Disabled tab
41437 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41438 tab4.disable();
41439
41440 jtabs.activate("jtabs-1");
41441  * </code></pre>
41442  * @constructor
41443  * Create a new TabPanel.
41444  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41445  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41446  */
41447 Roo.bootstrap.panel.Tabs = function(config){
41448     /**
41449     * The container element for this TabPanel.
41450     * @type Roo.Element
41451     */
41452     this.el = Roo.get(config.el);
41453     delete config.el;
41454     if(config){
41455         if(typeof config == "boolean"){
41456             this.tabPosition = config ? "bottom" : "top";
41457         }else{
41458             Roo.apply(this, config);
41459         }
41460     }
41461     
41462     if(this.tabPosition == "bottom"){
41463         // if tabs are at the bottom = create the body first.
41464         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41465         this.el.addClass("roo-tabs-bottom");
41466     }
41467     // next create the tabs holders
41468     
41469     if (this.tabPosition == "west"){
41470         
41471         var reg = this.region; // fake it..
41472         while (reg) {
41473             if (!reg.mgr.parent) {
41474                 break;
41475             }
41476             reg = reg.mgr.parent.region;
41477         }
41478         Roo.log("got nest?");
41479         Roo.log(reg);
41480         if (reg.mgr.getRegion('west')) {
41481             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41482             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41483             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41484             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41485             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41486         
41487             
41488         }
41489         
41490         
41491     } else {
41492      
41493         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41494         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41495         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41496         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41497     }
41498     
41499     
41500     if(Roo.isIE){
41501         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41502     }
41503     
41504     // finally - if tabs are at the top, then create the body last..
41505     if(this.tabPosition != "bottom"){
41506         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41507          * @type Roo.Element
41508          */
41509         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41510         this.el.addClass("roo-tabs-top");
41511     }
41512     this.items = [];
41513
41514     this.bodyEl.setStyle("position", "relative");
41515
41516     this.active = null;
41517     this.activateDelegate = this.activate.createDelegate(this);
41518
41519     this.addEvents({
41520         /**
41521          * @event tabchange
41522          * Fires when the active tab changes
41523          * @param {Roo.TabPanel} this
41524          * @param {Roo.TabPanelItem} activePanel The new active tab
41525          */
41526         "tabchange": true,
41527         /**
41528          * @event beforetabchange
41529          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41530          * @param {Roo.TabPanel} this
41531          * @param {Object} e Set cancel to true on this object to cancel the tab change
41532          * @param {Roo.TabPanelItem} tab The tab being changed to
41533          */
41534         "beforetabchange" : true
41535     });
41536
41537     Roo.EventManager.onWindowResize(this.onResize, this);
41538     this.cpad = this.el.getPadding("lr");
41539     this.hiddenCount = 0;
41540
41541
41542     // toolbar on the tabbar support...
41543     if (this.toolbar) {
41544         alert("no toolbar support yet");
41545         this.toolbar  = false;
41546         /*
41547         var tcfg = this.toolbar;
41548         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41549         this.toolbar = new Roo.Toolbar(tcfg);
41550         if (Roo.isSafari) {
41551             var tbl = tcfg.container.child('table', true);
41552             tbl.setAttribute('width', '100%');
41553         }
41554         */
41555         
41556     }
41557    
41558
41559
41560     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41561 };
41562
41563 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41564     /*
41565      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41566      */
41567     tabPosition : "top",
41568     /*
41569      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41570      */
41571     currentTabWidth : 0,
41572     /*
41573      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41574      */
41575     minTabWidth : 40,
41576     /*
41577      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41578      */
41579     maxTabWidth : 250,
41580     /*
41581      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41582      */
41583     preferredTabWidth : 175,
41584     /*
41585      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41586      */
41587     resizeTabs : false,
41588     /*
41589      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41590      */
41591     monitorResize : true,
41592     /*
41593      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41594      */
41595     toolbar : false,  // set by caller..
41596     
41597     region : false, /// set by caller
41598     
41599     disableTooltips : true, // not used yet...
41600
41601     /**
41602      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41603      * @param {String} id The id of the div to use <b>or create</b>
41604      * @param {String} text The text for the tab
41605      * @param {String} content (optional) Content to put in the TabPanelItem body
41606      * @param {Boolean} closable (optional) True to create a close icon on the tab
41607      * @return {Roo.TabPanelItem} The created TabPanelItem
41608      */
41609     addTab : function(id, text, content, closable, tpl)
41610     {
41611         var item = new Roo.bootstrap.panel.TabItem({
41612             panel: this,
41613             id : id,
41614             text : text,
41615             closable : closable,
41616             tpl : tpl
41617         });
41618         this.addTabItem(item);
41619         if(content){
41620             item.setContent(content);
41621         }
41622         return item;
41623     },
41624
41625     /**
41626      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41627      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41628      * @return {Roo.TabPanelItem}
41629      */
41630     getTab : function(id){
41631         return this.items[id];
41632     },
41633
41634     /**
41635      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41636      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41637      */
41638     hideTab : function(id){
41639         var t = this.items[id];
41640         if(!t.isHidden()){
41641            t.setHidden(true);
41642            this.hiddenCount++;
41643            this.autoSizeTabs();
41644         }
41645     },
41646
41647     /**
41648      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41649      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41650      */
41651     unhideTab : function(id){
41652         var t = this.items[id];
41653         if(t.isHidden()){
41654            t.setHidden(false);
41655            this.hiddenCount--;
41656            this.autoSizeTabs();
41657         }
41658     },
41659
41660     /**
41661      * Adds an existing {@link Roo.TabPanelItem}.
41662      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41663      */
41664     addTabItem : function(item)
41665     {
41666         this.items[item.id] = item;
41667         this.items.push(item);
41668         this.autoSizeTabs();
41669       //  if(this.resizeTabs){
41670     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41671   //         this.autoSizeTabs();
41672 //        }else{
41673 //            item.autoSize();
41674        // }
41675     },
41676
41677     /**
41678      * Removes a {@link Roo.TabPanelItem}.
41679      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41680      */
41681     removeTab : function(id){
41682         var items = this.items;
41683         var tab = items[id];
41684         if(!tab) { return; }
41685         var index = items.indexOf(tab);
41686         if(this.active == tab && items.length > 1){
41687             var newTab = this.getNextAvailable(index);
41688             if(newTab) {
41689                 newTab.activate();
41690             }
41691         }
41692         this.stripEl.dom.removeChild(tab.pnode.dom);
41693         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41694             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41695         }
41696         items.splice(index, 1);
41697         delete this.items[tab.id];
41698         tab.fireEvent("close", tab);
41699         tab.purgeListeners();
41700         this.autoSizeTabs();
41701     },
41702
41703     getNextAvailable : function(start){
41704         var items = this.items;
41705         var index = start;
41706         // look for a next tab that will slide over to
41707         // replace the one being removed
41708         while(index < items.length){
41709             var item = items[++index];
41710             if(item && !item.isHidden()){
41711                 return item;
41712             }
41713         }
41714         // if one isn't found select the previous tab (on the left)
41715         index = start;
41716         while(index >= 0){
41717             var item = items[--index];
41718             if(item && !item.isHidden()){
41719                 return item;
41720             }
41721         }
41722         return null;
41723     },
41724
41725     /**
41726      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41727      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41728      */
41729     disableTab : function(id){
41730         var tab = this.items[id];
41731         if(tab && this.active != tab){
41732             tab.disable();
41733         }
41734     },
41735
41736     /**
41737      * Enables a {@link Roo.TabPanelItem} that is disabled.
41738      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41739      */
41740     enableTab : function(id){
41741         var tab = this.items[id];
41742         tab.enable();
41743     },
41744
41745     /**
41746      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41747      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41748      * @return {Roo.TabPanelItem} The TabPanelItem.
41749      */
41750     activate : function(id)
41751     {
41752         //Roo.log('activite:'  + id);
41753         
41754         var tab = this.items[id];
41755         if(!tab){
41756             return null;
41757         }
41758         if(tab == this.active || tab.disabled){
41759             return tab;
41760         }
41761         var e = {};
41762         this.fireEvent("beforetabchange", this, e, tab);
41763         if(e.cancel !== true && !tab.disabled){
41764             if(this.active){
41765                 this.active.hide();
41766             }
41767             this.active = this.items[id];
41768             this.active.show();
41769             this.fireEvent("tabchange", this, this.active);
41770         }
41771         return tab;
41772     },
41773
41774     /**
41775      * Gets the active {@link Roo.TabPanelItem}.
41776      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41777      */
41778     getActiveTab : function(){
41779         return this.active;
41780     },
41781
41782     /**
41783      * Updates the tab body element to fit the height of the container element
41784      * for overflow scrolling
41785      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41786      */
41787     syncHeight : function(targetHeight){
41788         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41789         var bm = this.bodyEl.getMargins();
41790         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41791         this.bodyEl.setHeight(newHeight);
41792         return newHeight;
41793     },
41794
41795     onResize : function(){
41796         if(this.monitorResize){
41797             this.autoSizeTabs();
41798         }
41799     },
41800
41801     /**
41802      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41803      */
41804     beginUpdate : function(){
41805         this.updating = true;
41806     },
41807
41808     /**
41809      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41810      */
41811     endUpdate : function(){
41812         this.updating = false;
41813         this.autoSizeTabs();
41814     },
41815
41816     /**
41817      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41818      */
41819     autoSizeTabs : function()
41820     {
41821         var count = this.items.length;
41822         var vcount = count - this.hiddenCount;
41823         
41824         if (vcount < 2) {
41825             this.stripEl.hide();
41826         } else {
41827             this.stripEl.show();
41828         }
41829         
41830         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41831             return;
41832         }
41833         
41834         
41835         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41836         var availWidth = Math.floor(w / vcount);
41837         var b = this.stripBody;
41838         if(b.getWidth() > w){
41839             var tabs = this.items;
41840             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41841             if(availWidth < this.minTabWidth){
41842                 /*if(!this.sleft){    // incomplete scrolling code
41843                     this.createScrollButtons();
41844                 }
41845                 this.showScroll();
41846                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41847             }
41848         }else{
41849             if(this.currentTabWidth < this.preferredTabWidth){
41850                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41851             }
41852         }
41853     },
41854
41855     /**
41856      * Returns the number of tabs in this TabPanel.
41857      * @return {Number}
41858      */
41859      getCount : function(){
41860          return this.items.length;
41861      },
41862
41863     /**
41864      * Resizes all the tabs to the passed width
41865      * @param {Number} The new width
41866      */
41867     setTabWidth : function(width){
41868         this.currentTabWidth = width;
41869         for(var i = 0, len = this.items.length; i < len; i++) {
41870                 if(!this.items[i].isHidden()) {
41871                 this.items[i].setWidth(width);
41872             }
41873         }
41874     },
41875
41876     /**
41877      * Destroys this TabPanel
41878      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41879      */
41880     destroy : function(removeEl){
41881         Roo.EventManager.removeResizeListener(this.onResize, this);
41882         for(var i = 0, len = this.items.length; i < len; i++){
41883             this.items[i].purgeListeners();
41884         }
41885         if(removeEl === true){
41886             this.el.update("");
41887             this.el.remove();
41888         }
41889     },
41890     
41891     createStrip : function(container)
41892     {
41893         var strip = document.createElement("nav");
41894         strip.className = Roo.bootstrap.version == 4 ?
41895             "navbar-light bg-light" : 
41896             "navbar navbar-default"; //"x-tabs-wrap";
41897         container.appendChild(strip);
41898         return strip;
41899     },
41900     
41901     createStripList : function(strip)
41902     {
41903         // div wrapper for retard IE
41904         // returns the "tr" element.
41905         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41906         //'<div class="x-tabs-strip-wrap">'+
41907           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41908           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41909         return strip.firstChild; //.firstChild.firstChild.firstChild;
41910     },
41911     createBody : function(container)
41912     {
41913         var body = document.createElement("div");
41914         Roo.id(body, "tab-body");
41915         //Roo.fly(body).addClass("x-tabs-body");
41916         Roo.fly(body).addClass("tab-content");
41917         container.appendChild(body);
41918         return body;
41919     },
41920     createItemBody :function(bodyEl, id){
41921         var body = Roo.getDom(id);
41922         if(!body){
41923             body = document.createElement("div");
41924             body.id = id;
41925         }
41926         //Roo.fly(body).addClass("x-tabs-item-body");
41927         Roo.fly(body).addClass("tab-pane");
41928          bodyEl.insertBefore(body, bodyEl.firstChild);
41929         return body;
41930     },
41931     /** @private */
41932     createStripElements :  function(stripEl, text, closable, tpl)
41933     {
41934         var td = document.createElement("li"); // was td..
41935         td.className = 'nav-item';
41936         
41937         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41938         
41939         
41940         stripEl.appendChild(td);
41941         /*if(closable){
41942             td.className = "x-tabs-closable";
41943             if(!this.closeTpl){
41944                 this.closeTpl = new Roo.Template(
41945                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41946                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41947                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41948                 );
41949             }
41950             var el = this.closeTpl.overwrite(td, {"text": text});
41951             var close = el.getElementsByTagName("div")[0];
41952             var inner = el.getElementsByTagName("em")[0];
41953             return {"el": el, "close": close, "inner": inner};
41954         } else {
41955         */
41956         // not sure what this is..
41957 //            if(!this.tabTpl){
41958                 //this.tabTpl = new Roo.Template(
41959                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41960                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41961                 //);
41962 //                this.tabTpl = new Roo.Template(
41963 //                   '<a href="#">' +
41964 //                   '<span unselectable="on"' +
41965 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41966 //                            ' >{text}</span></a>'
41967 //                );
41968 //                
41969 //            }
41970
41971
41972             var template = tpl || this.tabTpl || false;
41973             
41974             if(!template){
41975                 template =  new Roo.Template(
41976                         Roo.bootstrap.version == 4 ? 
41977                             (
41978                                 '<a class="nav-link" href="#" unselectable="on"' +
41979                                      (this.disableTooltips ? '' : ' title="{text}"') +
41980                                      ' >{text}</a>'
41981                             ) : (
41982                                 '<a class="nav-link" href="#">' +
41983                                 '<span unselectable="on"' +
41984                                          (this.disableTooltips ? '' : ' title="{text}"') +
41985                                     ' >{text}</span></a>'
41986                             )
41987                 );
41988             }
41989             
41990             switch (typeof(template)) {
41991                 case 'object' :
41992                     break;
41993                 case 'string' :
41994                     template = new Roo.Template(template);
41995                     break;
41996                 default :
41997                     break;
41998             }
41999             
42000             var el = template.overwrite(td, {"text": text});
42001             
42002             var inner = el.getElementsByTagName("span")[0];
42003             
42004             return {"el": el, "inner": inner};
42005             
42006     }
42007         
42008     
42009 });
42010
42011 /**
42012  * @class Roo.TabPanelItem
42013  * @extends Roo.util.Observable
42014  * Represents an individual item (tab plus body) in a TabPanel.
42015  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42016  * @param {String} id The id of this TabPanelItem
42017  * @param {String} text The text for the tab of this TabPanelItem
42018  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42019  */
42020 Roo.bootstrap.panel.TabItem = function(config){
42021     /**
42022      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42023      * @type Roo.TabPanel
42024      */
42025     this.tabPanel = config.panel;
42026     /**
42027      * The id for this TabPanelItem
42028      * @type String
42029      */
42030     this.id = config.id;
42031     /** @private */
42032     this.disabled = false;
42033     /** @private */
42034     this.text = config.text;
42035     /** @private */
42036     this.loaded = false;
42037     this.closable = config.closable;
42038
42039     /**
42040      * The body element for this TabPanelItem.
42041      * @type Roo.Element
42042      */
42043     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42044     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42045     this.bodyEl.setStyle("display", "block");
42046     this.bodyEl.setStyle("zoom", "1");
42047     //this.hideAction();
42048
42049     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42050     /** @private */
42051     this.el = Roo.get(els.el);
42052     this.inner = Roo.get(els.inner, true);
42053      this.textEl = Roo.bootstrap.version == 4 ?
42054         this.el : Roo.get(this.el.dom.firstChild, true);
42055
42056     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42057     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42058
42059     
42060 //    this.el.on("mousedown", this.onTabMouseDown, this);
42061     this.el.on("click", this.onTabClick, this);
42062     /** @private */
42063     if(config.closable){
42064         var c = Roo.get(els.close, true);
42065         c.dom.title = this.closeText;
42066         c.addClassOnOver("close-over");
42067         c.on("click", this.closeClick, this);
42068      }
42069
42070     this.addEvents({
42071          /**
42072          * @event activate
42073          * Fires when this tab becomes the active tab.
42074          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42075          * @param {Roo.TabPanelItem} this
42076          */
42077         "activate": true,
42078         /**
42079          * @event beforeclose
42080          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42081          * @param {Roo.TabPanelItem} this
42082          * @param {Object} e Set cancel to true on this object to cancel the close.
42083          */
42084         "beforeclose": true,
42085         /**
42086          * @event close
42087          * Fires when this tab is closed.
42088          * @param {Roo.TabPanelItem} this
42089          */
42090          "close": true,
42091         /**
42092          * @event deactivate
42093          * Fires when this tab is no longer the active tab.
42094          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42095          * @param {Roo.TabPanelItem} this
42096          */
42097          "deactivate" : true
42098     });
42099     this.hidden = false;
42100
42101     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42102 };
42103
42104 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42105            {
42106     purgeListeners : function(){
42107        Roo.util.Observable.prototype.purgeListeners.call(this);
42108        this.el.removeAllListeners();
42109     },
42110     /**
42111      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42112      */
42113     show : function(){
42114         this.status_node.addClass("active");
42115         this.showAction();
42116         if(Roo.isOpera){
42117             this.tabPanel.stripWrap.repaint();
42118         }
42119         this.fireEvent("activate", this.tabPanel, this);
42120     },
42121
42122     /**
42123      * Returns true if this tab is the active tab.
42124      * @return {Boolean}
42125      */
42126     isActive : function(){
42127         return this.tabPanel.getActiveTab() == this;
42128     },
42129
42130     /**
42131      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42132      */
42133     hide : function(){
42134         this.status_node.removeClass("active");
42135         this.hideAction();
42136         this.fireEvent("deactivate", this.tabPanel, this);
42137     },
42138
42139     hideAction : function(){
42140         this.bodyEl.hide();
42141         this.bodyEl.setStyle("position", "absolute");
42142         this.bodyEl.setLeft("-20000px");
42143         this.bodyEl.setTop("-20000px");
42144     },
42145
42146     showAction : function(){
42147         this.bodyEl.setStyle("position", "relative");
42148         this.bodyEl.setTop("");
42149         this.bodyEl.setLeft("");
42150         this.bodyEl.show();
42151     },
42152
42153     /**
42154      * Set the tooltip for the tab.
42155      * @param {String} tooltip The tab's tooltip
42156      */
42157     setTooltip : function(text){
42158         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42159             this.textEl.dom.qtip = text;
42160             this.textEl.dom.removeAttribute('title');
42161         }else{
42162             this.textEl.dom.title = text;
42163         }
42164     },
42165
42166     onTabClick : function(e){
42167         e.preventDefault();
42168         this.tabPanel.activate(this.id);
42169     },
42170
42171     onTabMouseDown : function(e){
42172         e.preventDefault();
42173         this.tabPanel.activate(this.id);
42174     },
42175 /*
42176     getWidth : function(){
42177         return this.inner.getWidth();
42178     },
42179
42180     setWidth : function(width){
42181         var iwidth = width - this.linode.getPadding("lr");
42182         this.inner.setWidth(iwidth);
42183         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42184         this.linode.setWidth(width);
42185     },
42186 */
42187     /**
42188      * Show or hide the tab
42189      * @param {Boolean} hidden True to hide or false to show.
42190      */
42191     setHidden : function(hidden){
42192         this.hidden = hidden;
42193         this.linode.setStyle("display", hidden ? "none" : "");
42194     },
42195
42196     /**
42197      * Returns true if this tab is "hidden"
42198      * @return {Boolean}
42199      */
42200     isHidden : function(){
42201         return this.hidden;
42202     },
42203
42204     /**
42205      * Returns the text for this tab
42206      * @return {String}
42207      */
42208     getText : function(){
42209         return this.text;
42210     },
42211     /*
42212     autoSize : function(){
42213         //this.el.beginMeasure();
42214         this.textEl.setWidth(1);
42215         /*
42216          *  #2804 [new] Tabs in Roojs
42217          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42218          */
42219         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42220         //this.el.endMeasure();
42221     //},
42222
42223     /**
42224      * Sets the text for the tab (Note: this also sets the tooltip text)
42225      * @param {String} text The tab's text and tooltip
42226      */
42227     setText : function(text){
42228         this.text = text;
42229         this.textEl.update(text);
42230         this.setTooltip(text);
42231         //if(!this.tabPanel.resizeTabs){
42232         //    this.autoSize();
42233         //}
42234     },
42235     /**
42236      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42237      */
42238     activate : function(){
42239         this.tabPanel.activate(this.id);
42240     },
42241
42242     /**
42243      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42244      */
42245     disable : function(){
42246         if(this.tabPanel.active != this){
42247             this.disabled = true;
42248             this.status_node.addClass("disabled");
42249         }
42250     },
42251
42252     /**
42253      * Enables this TabPanelItem if it was previously disabled.
42254      */
42255     enable : function(){
42256         this.disabled = false;
42257         this.status_node.removeClass("disabled");
42258     },
42259
42260     /**
42261      * Sets the content for this TabPanelItem.
42262      * @param {String} content The content
42263      * @param {Boolean} loadScripts true to look for and load scripts
42264      */
42265     setContent : function(content, loadScripts){
42266         this.bodyEl.update(content, loadScripts);
42267     },
42268
42269     /**
42270      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42271      * @return {Roo.UpdateManager} The UpdateManager
42272      */
42273     getUpdateManager : function(){
42274         return this.bodyEl.getUpdateManager();
42275     },
42276
42277     /**
42278      * Set a URL to be used to load the content for this TabPanelItem.
42279      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42280      * @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)
42281      * @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)
42282      * @return {Roo.UpdateManager} The UpdateManager
42283      */
42284     setUrl : function(url, params, loadOnce){
42285         if(this.refreshDelegate){
42286             this.un('activate', this.refreshDelegate);
42287         }
42288         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42289         this.on("activate", this.refreshDelegate);
42290         return this.bodyEl.getUpdateManager();
42291     },
42292
42293     /** @private */
42294     _handleRefresh : function(url, params, loadOnce){
42295         if(!loadOnce || !this.loaded){
42296             var updater = this.bodyEl.getUpdateManager();
42297             updater.update(url, params, this._setLoaded.createDelegate(this));
42298         }
42299     },
42300
42301     /**
42302      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42303      *   Will fail silently if the setUrl method has not been called.
42304      *   This does not activate the panel, just updates its content.
42305      */
42306     refresh : function(){
42307         if(this.refreshDelegate){
42308            this.loaded = false;
42309            this.refreshDelegate();
42310         }
42311     },
42312
42313     /** @private */
42314     _setLoaded : function(){
42315         this.loaded = true;
42316     },
42317
42318     /** @private */
42319     closeClick : function(e){
42320         var o = {};
42321         e.stopEvent();
42322         this.fireEvent("beforeclose", this, o);
42323         if(o.cancel !== true){
42324             this.tabPanel.removeTab(this.id);
42325         }
42326     },
42327     /**
42328      * The text displayed in the tooltip for the close icon.
42329      * @type String
42330      */
42331     closeText : "Close this tab"
42332 });
42333 /**
42334 *    This script refer to:
42335 *    Title: International Telephone Input
42336 *    Author: Jack O'Connor
42337 *    Code version:  v12.1.12
42338 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42339 **/
42340
42341 Roo.bootstrap.PhoneInputData = function() {
42342     var d = [
42343       [
42344         "Afghanistan (‫افغانستان‬‎)",
42345         "af",
42346         "93"
42347       ],
42348       [
42349         "Albania (Shqipëri)",
42350         "al",
42351         "355"
42352       ],
42353       [
42354         "Algeria (‫الجزائر‬‎)",
42355         "dz",
42356         "213"
42357       ],
42358       [
42359         "American Samoa",
42360         "as",
42361         "1684"
42362       ],
42363       [
42364         "Andorra",
42365         "ad",
42366         "376"
42367       ],
42368       [
42369         "Angola",
42370         "ao",
42371         "244"
42372       ],
42373       [
42374         "Anguilla",
42375         "ai",
42376         "1264"
42377       ],
42378       [
42379         "Antigua and Barbuda",
42380         "ag",
42381         "1268"
42382       ],
42383       [
42384         "Argentina",
42385         "ar",
42386         "54"
42387       ],
42388       [
42389         "Armenia (Հայաստան)",
42390         "am",
42391         "374"
42392       ],
42393       [
42394         "Aruba",
42395         "aw",
42396         "297"
42397       ],
42398       [
42399         "Australia",
42400         "au",
42401         "61",
42402         0
42403       ],
42404       [
42405         "Austria (Österreich)",
42406         "at",
42407         "43"
42408       ],
42409       [
42410         "Azerbaijan (Azərbaycan)",
42411         "az",
42412         "994"
42413       ],
42414       [
42415         "Bahamas",
42416         "bs",
42417         "1242"
42418       ],
42419       [
42420         "Bahrain (‫البحرين‬‎)",
42421         "bh",
42422         "973"
42423       ],
42424       [
42425         "Bangladesh (বাংলাদেশ)",
42426         "bd",
42427         "880"
42428       ],
42429       [
42430         "Barbados",
42431         "bb",
42432         "1246"
42433       ],
42434       [
42435         "Belarus (Беларусь)",
42436         "by",
42437         "375"
42438       ],
42439       [
42440         "Belgium (België)",
42441         "be",
42442         "32"
42443       ],
42444       [
42445         "Belize",
42446         "bz",
42447         "501"
42448       ],
42449       [
42450         "Benin (Bénin)",
42451         "bj",
42452         "229"
42453       ],
42454       [
42455         "Bermuda",
42456         "bm",
42457         "1441"
42458       ],
42459       [
42460         "Bhutan (འབྲུག)",
42461         "bt",
42462         "975"
42463       ],
42464       [
42465         "Bolivia",
42466         "bo",
42467         "591"
42468       ],
42469       [
42470         "Bosnia and Herzegovina (Босна и Херцеговина)",
42471         "ba",
42472         "387"
42473       ],
42474       [
42475         "Botswana",
42476         "bw",
42477         "267"
42478       ],
42479       [
42480         "Brazil (Brasil)",
42481         "br",
42482         "55"
42483       ],
42484       [
42485         "British Indian Ocean Territory",
42486         "io",
42487         "246"
42488       ],
42489       [
42490         "British Virgin Islands",
42491         "vg",
42492         "1284"
42493       ],
42494       [
42495         "Brunei",
42496         "bn",
42497         "673"
42498       ],
42499       [
42500         "Bulgaria (България)",
42501         "bg",
42502         "359"
42503       ],
42504       [
42505         "Burkina Faso",
42506         "bf",
42507         "226"
42508       ],
42509       [
42510         "Burundi (Uburundi)",
42511         "bi",
42512         "257"
42513       ],
42514       [
42515         "Cambodia (កម្ពុជា)",
42516         "kh",
42517         "855"
42518       ],
42519       [
42520         "Cameroon (Cameroun)",
42521         "cm",
42522         "237"
42523       ],
42524       [
42525         "Canada",
42526         "ca",
42527         "1",
42528         1,
42529         ["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"]
42530       ],
42531       [
42532         "Cape Verde (Kabu Verdi)",
42533         "cv",
42534         "238"
42535       ],
42536       [
42537         "Caribbean Netherlands",
42538         "bq",
42539         "599",
42540         1
42541       ],
42542       [
42543         "Cayman Islands",
42544         "ky",
42545         "1345"
42546       ],
42547       [
42548         "Central African Republic (République centrafricaine)",
42549         "cf",
42550         "236"
42551       ],
42552       [
42553         "Chad (Tchad)",
42554         "td",
42555         "235"
42556       ],
42557       [
42558         "Chile",
42559         "cl",
42560         "56"
42561       ],
42562       [
42563         "China (中国)",
42564         "cn",
42565         "86"
42566       ],
42567       [
42568         "Christmas Island",
42569         "cx",
42570         "61",
42571         2
42572       ],
42573       [
42574         "Cocos (Keeling) Islands",
42575         "cc",
42576         "61",
42577         1
42578       ],
42579       [
42580         "Colombia",
42581         "co",
42582         "57"
42583       ],
42584       [
42585         "Comoros (‫جزر القمر‬‎)",
42586         "km",
42587         "269"
42588       ],
42589       [
42590         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42591         "cd",
42592         "243"
42593       ],
42594       [
42595         "Congo (Republic) (Congo-Brazzaville)",
42596         "cg",
42597         "242"
42598       ],
42599       [
42600         "Cook Islands",
42601         "ck",
42602         "682"
42603       ],
42604       [
42605         "Costa Rica",
42606         "cr",
42607         "506"
42608       ],
42609       [
42610         "Côte d’Ivoire",
42611         "ci",
42612         "225"
42613       ],
42614       [
42615         "Croatia (Hrvatska)",
42616         "hr",
42617         "385"
42618       ],
42619       [
42620         "Cuba",
42621         "cu",
42622         "53"
42623       ],
42624       [
42625         "Curaçao",
42626         "cw",
42627         "599",
42628         0
42629       ],
42630       [
42631         "Cyprus (Κύπρος)",
42632         "cy",
42633         "357"
42634       ],
42635       [
42636         "Czech Republic (Česká republika)",
42637         "cz",
42638         "420"
42639       ],
42640       [
42641         "Denmark (Danmark)",
42642         "dk",
42643         "45"
42644       ],
42645       [
42646         "Djibouti",
42647         "dj",
42648         "253"
42649       ],
42650       [
42651         "Dominica",
42652         "dm",
42653         "1767"
42654       ],
42655       [
42656         "Dominican Republic (República Dominicana)",
42657         "do",
42658         "1",
42659         2,
42660         ["809", "829", "849"]
42661       ],
42662       [
42663         "Ecuador",
42664         "ec",
42665         "593"
42666       ],
42667       [
42668         "Egypt (‫مصر‬‎)",
42669         "eg",
42670         "20"
42671       ],
42672       [
42673         "El Salvador",
42674         "sv",
42675         "503"
42676       ],
42677       [
42678         "Equatorial Guinea (Guinea Ecuatorial)",
42679         "gq",
42680         "240"
42681       ],
42682       [
42683         "Eritrea",
42684         "er",
42685         "291"
42686       ],
42687       [
42688         "Estonia (Eesti)",
42689         "ee",
42690         "372"
42691       ],
42692       [
42693         "Ethiopia",
42694         "et",
42695         "251"
42696       ],
42697       [
42698         "Falkland Islands (Islas Malvinas)",
42699         "fk",
42700         "500"
42701       ],
42702       [
42703         "Faroe Islands (Føroyar)",
42704         "fo",
42705         "298"
42706       ],
42707       [
42708         "Fiji",
42709         "fj",
42710         "679"
42711       ],
42712       [
42713         "Finland (Suomi)",
42714         "fi",
42715         "358",
42716         0
42717       ],
42718       [
42719         "France",
42720         "fr",
42721         "33"
42722       ],
42723       [
42724         "French Guiana (Guyane française)",
42725         "gf",
42726         "594"
42727       ],
42728       [
42729         "French Polynesia (Polynésie française)",
42730         "pf",
42731         "689"
42732       ],
42733       [
42734         "Gabon",
42735         "ga",
42736         "241"
42737       ],
42738       [
42739         "Gambia",
42740         "gm",
42741         "220"
42742       ],
42743       [
42744         "Georgia (საქართველო)",
42745         "ge",
42746         "995"
42747       ],
42748       [
42749         "Germany (Deutschland)",
42750         "de",
42751         "49"
42752       ],
42753       [
42754         "Ghana (Gaana)",
42755         "gh",
42756         "233"
42757       ],
42758       [
42759         "Gibraltar",
42760         "gi",
42761         "350"
42762       ],
42763       [
42764         "Greece (Ελλάδα)",
42765         "gr",
42766         "30"
42767       ],
42768       [
42769         "Greenland (Kalaallit Nunaat)",
42770         "gl",
42771         "299"
42772       ],
42773       [
42774         "Grenada",
42775         "gd",
42776         "1473"
42777       ],
42778       [
42779         "Guadeloupe",
42780         "gp",
42781         "590",
42782         0
42783       ],
42784       [
42785         "Guam",
42786         "gu",
42787         "1671"
42788       ],
42789       [
42790         "Guatemala",
42791         "gt",
42792         "502"
42793       ],
42794       [
42795         "Guernsey",
42796         "gg",
42797         "44",
42798         1
42799       ],
42800       [
42801         "Guinea (Guinée)",
42802         "gn",
42803         "224"
42804       ],
42805       [
42806         "Guinea-Bissau (Guiné Bissau)",
42807         "gw",
42808         "245"
42809       ],
42810       [
42811         "Guyana",
42812         "gy",
42813         "592"
42814       ],
42815       [
42816         "Haiti",
42817         "ht",
42818         "509"
42819       ],
42820       [
42821         "Honduras",
42822         "hn",
42823         "504"
42824       ],
42825       [
42826         "Hong Kong (香港)",
42827         "hk",
42828         "852"
42829       ],
42830       [
42831         "Hungary (Magyarország)",
42832         "hu",
42833         "36"
42834       ],
42835       [
42836         "Iceland (Ísland)",
42837         "is",
42838         "354"
42839       ],
42840       [
42841         "India (भारत)",
42842         "in",
42843         "91"
42844       ],
42845       [
42846         "Indonesia",
42847         "id",
42848         "62"
42849       ],
42850       [
42851         "Iran (‫ایران‬‎)",
42852         "ir",
42853         "98"
42854       ],
42855       [
42856         "Iraq (‫العراق‬‎)",
42857         "iq",
42858         "964"
42859       ],
42860       [
42861         "Ireland",
42862         "ie",
42863         "353"
42864       ],
42865       [
42866         "Isle of Man",
42867         "im",
42868         "44",
42869         2
42870       ],
42871       [
42872         "Israel (‫ישראל‬‎)",
42873         "il",
42874         "972"
42875       ],
42876       [
42877         "Italy (Italia)",
42878         "it",
42879         "39",
42880         0
42881       ],
42882       [
42883         "Jamaica",
42884         "jm",
42885         "1876"
42886       ],
42887       [
42888         "Japan (日本)",
42889         "jp",
42890         "81"
42891       ],
42892       [
42893         "Jersey",
42894         "je",
42895         "44",
42896         3
42897       ],
42898       [
42899         "Jordan (‫الأردن‬‎)",
42900         "jo",
42901         "962"
42902       ],
42903       [
42904         "Kazakhstan (Казахстан)",
42905         "kz",
42906         "7",
42907         1
42908       ],
42909       [
42910         "Kenya",
42911         "ke",
42912         "254"
42913       ],
42914       [
42915         "Kiribati",
42916         "ki",
42917         "686"
42918       ],
42919       [
42920         "Kosovo",
42921         "xk",
42922         "383"
42923       ],
42924       [
42925         "Kuwait (‫الكويت‬‎)",
42926         "kw",
42927         "965"
42928       ],
42929       [
42930         "Kyrgyzstan (Кыргызстан)",
42931         "kg",
42932         "996"
42933       ],
42934       [
42935         "Laos (ລາວ)",
42936         "la",
42937         "856"
42938       ],
42939       [
42940         "Latvia (Latvija)",
42941         "lv",
42942         "371"
42943       ],
42944       [
42945         "Lebanon (‫لبنان‬‎)",
42946         "lb",
42947         "961"
42948       ],
42949       [
42950         "Lesotho",
42951         "ls",
42952         "266"
42953       ],
42954       [
42955         "Liberia",
42956         "lr",
42957         "231"
42958       ],
42959       [
42960         "Libya (‫ليبيا‬‎)",
42961         "ly",
42962         "218"
42963       ],
42964       [
42965         "Liechtenstein",
42966         "li",
42967         "423"
42968       ],
42969       [
42970         "Lithuania (Lietuva)",
42971         "lt",
42972         "370"
42973       ],
42974       [
42975         "Luxembourg",
42976         "lu",
42977         "352"
42978       ],
42979       [
42980         "Macau (澳門)",
42981         "mo",
42982         "853"
42983       ],
42984       [
42985         "Macedonia (FYROM) (Македонија)",
42986         "mk",
42987         "389"
42988       ],
42989       [
42990         "Madagascar (Madagasikara)",
42991         "mg",
42992         "261"
42993       ],
42994       [
42995         "Malawi",
42996         "mw",
42997         "265"
42998       ],
42999       [
43000         "Malaysia",
43001         "my",
43002         "60"
43003       ],
43004       [
43005         "Maldives",
43006         "mv",
43007         "960"
43008       ],
43009       [
43010         "Mali",
43011         "ml",
43012         "223"
43013       ],
43014       [
43015         "Malta",
43016         "mt",
43017         "356"
43018       ],
43019       [
43020         "Marshall Islands",
43021         "mh",
43022         "692"
43023       ],
43024       [
43025         "Martinique",
43026         "mq",
43027         "596"
43028       ],
43029       [
43030         "Mauritania (‫موريتانيا‬‎)",
43031         "mr",
43032         "222"
43033       ],
43034       [
43035         "Mauritius (Moris)",
43036         "mu",
43037         "230"
43038       ],
43039       [
43040         "Mayotte",
43041         "yt",
43042         "262",
43043         1
43044       ],
43045       [
43046         "Mexico (México)",
43047         "mx",
43048         "52"
43049       ],
43050       [
43051         "Micronesia",
43052         "fm",
43053         "691"
43054       ],
43055       [
43056         "Moldova (Republica Moldova)",
43057         "md",
43058         "373"
43059       ],
43060       [
43061         "Monaco",
43062         "mc",
43063         "377"
43064       ],
43065       [
43066         "Mongolia (Монгол)",
43067         "mn",
43068         "976"
43069       ],
43070       [
43071         "Montenegro (Crna Gora)",
43072         "me",
43073         "382"
43074       ],
43075       [
43076         "Montserrat",
43077         "ms",
43078         "1664"
43079       ],
43080       [
43081         "Morocco (‫المغرب‬‎)",
43082         "ma",
43083         "212",
43084         0
43085       ],
43086       [
43087         "Mozambique (Moçambique)",
43088         "mz",
43089         "258"
43090       ],
43091       [
43092         "Myanmar (Burma) (မြန်မာ)",
43093         "mm",
43094         "95"
43095       ],
43096       [
43097         "Namibia (Namibië)",
43098         "na",
43099         "264"
43100       ],
43101       [
43102         "Nauru",
43103         "nr",
43104         "674"
43105       ],
43106       [
43107         "Nepal (नेपाल)",
43108         "np",
43109         "977"
43110       ],
43111       [
43112         "Netherlands (Nederland)",
43113         "nl",
43114         "31"
43115       ],
43116       [
43117         "New Caledonia (Nouvelle-Calédonie)",
43118         "nc",
43119         "687"
43120       ],
43121       [
43122         "New Zealand",
43123         "nz",
43124         "64"
43125       ],
43126       [
43127         "Nicaragua",
43128         "ni",
43129         "505"
43130       ],
43131       [
43132         "Niger (Nijar)",
43133         "ne",
43134         "227"
43135       ],
43136       [
43137         "Nigeria",
43138         "ng",
43139         "234"
43140       ],
43141       [
43142         "Niue",
43143         "nu",
43144         "683"
43145       ],
43146       [
43147         "Norfolk Island",
43148         "nf",
43149         "672"
43150       ],
43151       [
43152         "North Korea (조선 민주주의 인민 공화국)",
43153         "kp",
43154         "850"
43155       ],
43156       [
43157         "Northern Mariana Islands",
43158         "mp",
43159         "1670"
43160       ],
43161       [
43162         "Norway (Norge)",
43163         "no",
43164         "47",
43165         0
43166       ],
43167       [
43168         "Oman (‫عُمان‬‎)",
43169         "om",
43170         "968"
43171       ],
43172       [
43173         "Pakistan (‫پاکستان‬‎)",
43174         "pk",
43175         "92"
43176       ],
43177       [
43178         "Palau",
43179         "pw",
43180         "680"
43181       ],
43182       [
43183         "Palestine (‫فلسطين‬‎)",
43184         "ps",
43185         "970"
43186       ],
43187       [
43188         "Panama (Panamá)",
43189         "pa",
43190         "507"
43191       ],
43192       [
43193         "Papua New Guinea",
43194         "pg",
43195         "675"
43196       ],
43197       [
43198         "Paraguay",
43199         "py",
43200         "595"
43201       ],
43202       [
43203         "Peru (Perú)",
43204         "pe",
43205         "51"
43206       ],
43207       [
43208         "Philippines",
43209         "ph",
43210         "63"
43211       ],
43212       [
43213         "Poland (Polska)",
43214         "pl",
43215         "48"
43216       ],
43217       [
43218         "Portugal",
43219         "pt",
43220         "351"
43221       ],
43222       [
43223         "Puerto Rico",
43224         "pr",
43225         "1",
43226         3,
43227         ["787", "939"]
43228       ],
43229       [
43230         "Qatar (‫قطر‬‎)",
43231         "qa",
43232         "974"
43233       ],
43234       [
43235         "Réunion (La Réunion)",
43236         "re",
43237         "262",
43238         0
43239       ],
43240       [
43241         "Romania (România)",
43242         "ro",
43243         "40"
43244       ],
43245       [
43246         "Russia (Россия)",
43247         "ru",
43248         "7",
43249         0
43250       ],
43251       [
43252         "Rwanda",
43253         "rw",
43254         "250"
43255       ],
43256       [
43257         "Saint Barthélemy",
43258         "bl",
43259         "590",
43260         1
43261       ],
43262       [
43263         "Saint Helena",
43264         "sh",
43265         "290"
43266       ],
43267       [
43268         "Saint Kitts and Nevis",
43269         "kn",
43270         "1869"
43271       ],
43272       [
43273         "Saint Lucia",
43274         "lc",
43275         "1758"
43276       ],
43277       [
43278         "Saint Martin (Saint-Martin (partie française))",
43279         "mf",
43280         "590",
43281         2
43282       ],
43283       [
43284         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43285         "pm",
43286         "508"
43287       ],
43288       [
43289         "Saint Vincent and the Grenadines",
43290         "vc",
43291         "1784"
43292       ],
43293       [
43294         "Samoa",
43295         "ws",
43296         "685"
43297       ],
43298       [
43299         "San Marino",
43300         "sm",
43301         "378"
43302       ],
43303       [
43304         "São Tomé and Príncipe (São Tomé e Príncipe)",
43305         "st",
43306         "239"
43307       ],
43308       [
43309         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43310         "sa",
43311         "966"
43312       ],
43313       [
43314         "Senegal (Sénégal)",
43315         "sn",
43316         "221"
43317       ],
43318       [
43319         "Serbia (Србија)",
43320         "rs",
43321         "381"
43322       ],
43323       [
43324         "Seychelles",
43325         "sc",
43326         "248"
43327       ],
43328       [
43329         "Sierra Leone",
43330         "sl",
43331         "232"
43332       ],
43333       [
43334         "Singapore",
43335         "sg",
43336         "65"
43337       ],
43338       [
43339         "Sint Maarten",
43340         "sx",
43341         "1721"
43342       ],
43343       [
43344         "Slovakia (Slovensko)",
43345         "sk",
43346         "421"
43347       ],
43348       [
43349         "Slovenia (Slovenija)",
43350         "si",
43351         "386"
43352       ],
43353       [
43354         "Solomon Islands",
43355         "sb",
43356         "677"
43357       ],
43358       [
43359         "Somalia (Soomaaliya)",
43360         "so",
43361         "252"
43362       ],
43363       [
43364         "South Africa",
43365         "za",
43366         "27"
43367       ],
43368       [
43369         "South Korea (대한민국)",
43370         "kr",
43371         "82"
43372       ],
43373       [
43374         "South Sudan (‫جنوب السودان‬‎)",
43375         "ss",
43376         "211"
43377       ],
43378       [
43379         "Spain (España)",
43380         "es",
43381         "34"
43382       ],
43383       [
43384         "Sri Lanka (ශ්‍රී ලංකාව)",
43385         "lk",
43386         "94"
43387       ],
43388       [
43389         "Sudan (‫السودان‬‎)",
43390         "sd",
43391         "249"
43392       ],
43393       [
43394         "Suriname",
43395         "sr",
43396         "597"
43397       ],
43398       [
43399         "Svalbard and Jan Mayen",
43400         "sj",
43401         "47",
43402         1
43403       ],
43404       [
43405         "Swaziland",
43406         "sz",
43407         "268"
43408       ],
43409       [
43410         "Sweden (Sverige)",
43411         "se",
43412         "46"
43413       ],
43414       [
43415         "Switzerland (Schweiz)",
43416         "ch",
43417         "41"
43418       ],
43419       [
43420         "Syria (‫سوريا‬‎)",
43421         "sy",
43422         "963"
43423       ],
43424       [
43425         "Taiwan (台灣)",
43426         "tw",
43427         "886"
43428       ],
43429       [
43430         "Tajikistan",
43431         "tj",
43432         "992"
43433       ],
43434       [
43435         "Tanzania",
43436         "tz",
43437         "255"
43438       ],
43439       [
43440         "Thailand (ไทย)",
43441         "th",
43442         "66"
43443       ],
43444       [
43445         "Timor-Leste",
43446         "tl",
43447         "670"
43448       ],
43449       [
43450         "Togo",
43451         "tg",
43452         "228"
43453       ],
43454       [
43455         "Tokelau",
43456         "tk",
43457         "690"
43458       ],
43459       [
43460         "Tonga",
43461         "to",
43462         "676"
43463       ],
43464       [
43465         "Trinidad and Tobago",
43466         "tt",
43467         "1868"
43468       ],
43469       [
43470         "Tunisia (‫تونس‬‎)",
43471         "tn",
43472         "216"
43473       ],
43474       [
43475         "Turkey (Türkiye)",
43476         "tr",
43477         "90"
43478       ],
43479       [
43480         "Turkmenistan",
43481         "tm",
43482         "993"
43483       ],
43484       [
43485         "Turks and Caicos Islands",
43486         "tc",
43487         "1649"
43488       ],
43489       [
43490         "Tuvalu",
43491         "tv",
43492         "688"
43493       ],
43494       [
43495         "U.S. Virgin Islands",
43496         "vi",
43497         "1340"
43498       ],
43499       [
43500         "Uganda",
43501         "ug",
43502         "256"
43503       ],
43504       [
43505         "Ukraine (Україна)",
43506         "ua",
43507         "380"
43508       ],
43509       [
43510         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43511         "ae",
43512         "971"
43513       ],
43514       [
43515         "United Kingdom",
43516         "gb",
43517         "44",
43518         0
43519       ],
43520       [
43521         "United States",
43522         "us",
43523         "1",
43524         0
43525       ],
43526       [
43527         "Uruguay",
43528         "uy",
43529         "598"
43530       ],
43531       [
43532         "Uzbekistan (Oʻzbekiston)",
43533         "uz",
43534         "998"
43535       ],
43536       [
43537         "Vanuatu",
43538         "vu",
43539         "678"
43540       ],
43541       [
43542         "Vatican City (Città del Vaticano)",
43543         "va",
43544         "39",
43545         1
43546       ],
43547       [
43548         "Venezuela",
43549         "ve",
43550         "58"
43551       ],
43552       [
43553         "Vietnam (Việt Nam)",
43554         "vn",
43555         "84"
43556       ],
43557       [
43558         "Wallis and Futuna (Wallis-et-Futuna)",
43559         "wf",
43560         "681"
43561       ],
43562       [
43563         "Western Sahara (‫الصحراء الغربية‬‎)",
43564         "eh",
43565         "212",
43566         1
43567       ],
43568       [
43569         "Yemen (‫اليمن‬‎)",
43570         "ye",
43571         "967"
43572       ],
43573       [
43574         "Zambia",
43575         "zm",
43576         "260"
43577       ],
43578       [
43579         "Zimbabwe",
43580         "zw",
43581         "263"
43582       ],
43583       [
43584         "Åland Islands",
43585         "ax",
43586         "358",
43587         1
43588       ]
43589   ];
43590   
43591   return d;
43592 }/**
43593 *    This script refer to:
43594 *    Title: International Telephone Input
43595 *    Author: Jack O'Connor
43596 *    Code version:  v12.1.12
43597 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43598 **/
43599
43600 /**
43601  * @class Roo.bootstrap.PhoneInput
43602  * @extends Roo.bootstrap.TriggerField
43603  * An input with International dial-code selection
43604  
43605  * @cfg {String} defaultDialCode default '+852'
43606  * @cfg {Array} preferedCountries default []
43607   
43608  * @constructor
43609  * Create a new PhoneInput.
43610  * @param {Object} config Configuration options
43611  */
43612
43613 Roo.bootstrap.PhoneInput = function(config) {
43614     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43615 };
43616
43617 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43618         
43619         listWidth: undefined,
43620         
43621         selectedClass: 'active',
43622         
43623         invalidClass : "has-warning",
43624         
43625         validClass: 'has-success',
43626         
43627         allowed: '0123456789',
43628         
43629         max_length: 15,
43630         
43631         /**
43632          * @cfg {String} defaultDialCode The default dial code when initializing the input
43633          */
43634         defaultDialCode: '+852',
43635         
43636         /**
43637          * @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
43638          */
43639         preferedCountries: false,
43640         
43641         getAutoCreate : function()
43642         {
43643             var data = Roo.bootstrap.PhoneInputData();
43644             var align = this.labelAlign || this.parentLabelAlign();
43645             var id = Roo.id();
43646             
43647             this.allCountries = [];
43648             this.dialCodeMapping = [];
43649             
43650             for (var i = 0; i < data.length; i++) {
43651               var c = data[i];
43652               this.allCountries[i] = {
43653                 name: c[0],
43654                 iso2: c[1],
43655                 dialCode: c[2],
43656                 priority: c[3] || 0,
43657                 areaCodes: c[4] || null
43658               };
43659               this.dialCodeMapping[c[2]] = {
43660                   name: c[0],
43661                   iso2: c[1],
43662                   priority: c[3] || 0,
43663                   areaCodes: c[4] || null
43664               };
43665             }
43666             
43667             var cfg = {
43668                 cls: 'form-group',
43669                 cn: []
43670             };
43671             
43672             var input =  {
43673                 tag: 'input',
43674                 id : id,
43675                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43676                 maxlength: this.max_length,
43677                 cls : 'form-control tel-input',
43678                 autocomplete: 'new-password'
43679             };
43680             
43681             var hiddenInput = {
43682                 tag: 'input',
43683                 type: 'hidden',
43684                 cls: 'hidden-tel-input'
43685             };
43686             
43687             if (this.name) {
43688                 hiddenInput.name = this.name;
43689             }
43690             
43691             if (this.disabled) {
43692                 input.disabled = true;
43693             }
43694             
43695             var flag_container = {
43696                 tag: 'div',
43697                 cls: 'flag-box',
43698                 cn: [
43699                     {
43700                         tag: 'div',
43701                         cls: 'flag'
43702                     },
43703                     {
43704                         tag: 'div',
43705                         cls: 'caret'
43706                     }
43707                 ]
43708             };
43709             
43710             var box = {
43711                 tag: 'div',
43712                 cls: this.hasFeedback ? 'has-feedback' : '',
43713                 cn: [
43714                     hiddenInput,
43715                     input,
43716                     {
43717                         tag: 'input',
43718                         cls: 'dial-code-holder',
43719                         disabled: true
43720                     }
43721                 ]
43722             };
43723             
43724             var container = {
43725                 cls: 'roo-select2-container input-group',
43726                 cn: [
43727                     flag_container,
43728                     box
43729                 ]
43730             };
43731             
43732             if (this.fieldLabel.length) {
43733                 var indicator = {
43734                     tag: 'i',
43735                     tooltip: 'This field is required'
43736                 };
43737                 
43738                 var label = {
43739                     tag: 'label',
43740                     'for':  id,
43741                     cls: 'control-label',
43742                     cn: []
43743                 };
43744                 
43745                 var label_text = {
43746                     tag: 'span',
43747                     html: this.fieldLabel
43748                 };
43749                 
43750                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43751                 label.cn = [
43752                     indicator,
43753                     label_text
43754                 ];
43755                 
43756                 if(this.indicatorpos == 'right') {
43757                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43758                     label.cn = [
43759                         label_text,
43760                         indicator
43761                     ];
43762                 }
43763                 
43764                 if(align == 'left') {
43765                     container = {
43766                         tag: 'div',
43767                         cn: [
43768                             container
43769                         ]
43770                     };
43771                     
43772                     if(this.labelWidth > 12){
43773                         label.style = "width: " + this.labelWidth + 'px';
43774                     }
43775                     if(this.labelWidth < 13 && this.labelmd == 0){
43776                         this.labelmd = this.labelWidth;
43777                     }
43778                     if(this.labellg > 0){
43779                         label.cls += ' col-lg-' + this.labellg;
43780                         input.cls += ' col-lg-' + (12 - this.labellg);
43781                     }
43782                     if(this.labelmd > 0){
43783                         label.cls += ' col-md-' + this.labelmd;
43784                         container.cls += ' col-md-' + (12 - this.labelmd);
43785                     }
43786                     if(this.labelsm > 0){
43787                         label.cls += ' col-sm-' + this.labelsm;
43788                         container.cls += ' col-sm-' + (12 - this.labelsm);
43789                     }
43790                     if(this.labelxs > 0){
43791                         label.cls += ' col-xs-' + this.labelxs;
43792                         container.cls += ' col-xs-' + (12 - this.labelxs);
43793                     }
43794                 }
43795             }
43796             
43797             cfg.cn = [
43798                 label,
43799                 container
43800             ];
43801             
43802             var settings = this;
43803             
43804             ['xs','sm','md','lg'].map(function(size){
43805                 if (settings[size]) {
43806                     cfg.cls += ' col-' + size + '-' + settings[size];
43807                 }
43808             });
43809             
43810             this.store = new Roo.data.Store({
43811                 proxy : new Roo.data.MemoryProxy({}),
43812                 reader : new Roo.data.JsonReader({
43813                     fields : [
43814                         {
43815                             'name' : 'name',
43816                             'type' : 'string'
43817                         },
43818                         {
43819                             'name' : 'iso2',
43820                             'type' : 'string'
43821                         },
43822                         {
43823                             'name' : 'dialCode',
43824                             'type' : 'string'
43825                         },
43826                         {
43827                             'name' : 'priority',
43828                             'type' : 'string'
43829                         },
43830                         {
43831                             'name' : 'areaCodes',
43832                             'type' : 'string'
43833                         }
43834                     ]
43835                 })
43836             });
43837             
43838             if(!this.preferedCountries) {
43839                 this.preferedCountries = [
43840                     'hk',
43841                     'gb',
43842                     'us'
43843                 ];
43844             }
43845             
43846             var p = this.preferedCountries.reverse();
43847             
43848             if(p) {
43849                 for (var i = 0; i < p.length; i++) {
43850                     for (var j = 0; j < this.allCountries.length; j++) {
43851                         if(this.allCountries[j].iso2 == p[i]) {
43852                             var t = this.allCountries[j];
43853                             this.allCountries.splice(j,1);
43854                             this.allCountries.unshift(t);
43855                         }
43856                     } 
43857                 }
43858             }
43859             
43860             this.store.proxy.data = {
43861                 success: true,
43862                 data: this.allCountries
43863             };
43864             
43865             return cfg;
43866         },
43867         
43868         initEvents : function()
43869         {
43870             this.createList();
43871             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43872             
43873             this.indicator = this.indicatorEl();
43874             this.flag = this.flagEl();
43875             this.dialCodeHolder = this.dialCodeHolderEl();
43876             
43877             this.trigger = this.el.select('div.flag-box',true).first();
43878             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43879             
43880             var _this = this;
43881             
43882             (function(){
43883                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43884                 _this.list.setWidth(lw);
43885             }).defer(100);
43886             
43887             this.list.on('mouseover', this.onViewOver, this);
43888             this.list.on('mousemove', this.onViewMove, this);
43889             this.inputEl().on("keyup", this.onKeyUp, this);
43890             this.inputEl().on("keypress", this.onKeyPress, this);
43891             
43892             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43893
43894             this.view = new Roo.View(this.list, this.tpl, {
43895                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43896             });
43897             
43898             this.view.on('click', this.onViewClick, this);
43899             this.setValue(this.defaultDialCode);
43900         },
43901         
43902         onTriggerClick : function(e)
43903         {
43904             Roo.log('trigger click');
43905             if(this.disabled){
43906                 return;
43907             }
43908             
43909             if(this.isExpanded()){
43910                 this.collapse();
43911                 this.hasFocus = false;
43912             }else {
43913                 this.store.load({});
43914                 this.hasFocus = true;
43915                 this.expand();
43916             }
43917         },
43918         
43919         isExpanded : function()
43920         {
43921             return this.list.isVisible();
43922         },
43923         
43924         collapse : function()
43925         {
43926             if(!this.isExpanded()){
43927                 return;
43928             }
43929             this.list.hide();
43930             Roo.get(document).un('mousedown', this.collapseIf, this);
43931             Roo.get(document).un('mousewheel', this.collapseIf, this);
43932             this.fireEvent('collapse', this);
43933             this.validate();
43934         },
43935         
43936         expand : function()
43937         {
43938             Roo.log('expand');
43939
43940             if(this.isExpanded() || !this.hasFocus){
43941                 return;
43942             }
43943             
43944             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43945             this.list.setWidth(lw);
43946             
43947             this.list.show();
43948             this.restrictHeight();
43949             
43950             Roo.get(document).on('mousedown', this.collapseIf, this);
43951             Roo.get(document).on('mousewheel', this.collapseIf, this);
43952             
43953             this.fireEvent('expand', this);
43954         },
43955         
43956         restrictHeight : function()
43957         {
43958             this.list.alignTo(this.inputEl(), this.listAlign);
43959             this.list.alignTo(this.inputEl(), this.listAlign);
43960         },
43961         
43962         onViewOver : function(e, t)
43963         {
43964             if(this.inKeyMode){
43965                 return;
43966             }
43967             var item = this.view.findItemFromChild(t);
43968             
43969             if(item){
43970                 var index = this.view.indexOf(item);
43971                 this.select(index, false);
43972             }
43973         },
43974
43975         // private
43976         onViewClick : function(view, doFocus, el, e)
43977         {
43978             var index = this.view.getSelectedIndexes()[0];
43979             
43980             var r = this.store.getAt(index);
43981             
43982             if(r){
43983                 this.onSelect(r, index);
43984             }
43985             if(doFocus !== false && !this.blockFocus){
43986                 this.inputEl().focus();
43987             }
43988         },
43989         
43990         onViewMove : function(e, t)
43991         {
43992             this.inKeyMode = false;
43993         },
43994         
43995         select : function(index, scrollIntoView)
43996         {
43997             this.selectedIndex = index;
43998             this.view.select(index);
43999             if(scrollIntoView !== false){
44000                 var el = this.view.getNode(index);
44001                 if(el){
44002                     this.list.scrollChildIntoView(el, false);
44003                 }
44004             }
44005         },
44006         
44007         createList : function()
44008         {
44009             this.list = Roo.get(document.body).createChild({
44010                 tag: 'ul',
44011                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44012                 style: 'display:none'
44013             });
44014             
44015             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44016         },
44017         
44018         collapseIf : function(e)
44019         {
44020             var in_combo  = e.within(this.el);
44021             var in_list =  e.within(this.list);
44022             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44023             
44024             if (in_combo || in_list || is_list) {
44025                 return;
44026             }
44027             this.collapse();
44028         },
44029         
44030         onSelect : function(record, index)
44031         {
44032             if(this.fireEvent('beforeselect', this, record, index) !== false){
44033                 
44034                 this.setFlagClass(record.data.iso2);
44035                 this.setDialCode(record.data.dialCode);
44036                 this.hasFocus = false;
44037                 this.collapse();
44038                 this.fireEvent('select', this, record, index);
44039             }
44040         },
44041         
44042         flagEl : function()
44043         {
44044             var flag = this.el.select('div.flag',true).first();
44045             if(!flag){
44046                 return false;
44047             }
44048             return flag;
44049         },
44050         
44051         dialCodeHolderEl : function()
44052         {
44053             var d = this.el.select('input.dial-code-holder',true).first();
44054             if(!d){
44055                 return false;
44056             }
44057             return d;
44058         },
44059         
44060         setDialCode : function(v)
44061         {
44062             this.dialCodeHolder.dom.value = '+'+v;
44063         },
44064         
44065         setFlagClass : function(n)
44066         {
44067             this.flag.dom.className = 'flag '+n;
44068         },
44069         
44070         getValue : function()
44071         {
44072             var v = this.inputEl().getValue();
44073             if(this.dialCodeHolder) {
44074                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44075             }
44076             return v;
44077         },
44078         
44079         setValue : function(v)
44080         {
44081             var d = this.getDialCode(v);
44082             
44083             //invalid dial code
44084             if(v.length == 0 || !d || d.length == 0) {
44085                 if(this.rendered){
44086                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44087                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44088                 }
44089                 return;
44090             }
44091             
44092             //valid dial code
44093             this.setFlagClass(this.dialCodeMapping[d].iso2);
44094             this.setDialCode(d);
44095             this.inputEl().dom.value = v.replace('+'+d,'');
44096             this.hiddenEl().dom.value = this.getValue();
44097             
44098             this.validate();
44099         },
44100         
44101         getDialCode : function(v)
44102         {
44103             v = v ||  '';
44104             
44105             if (v.length == 0) {
44106                 return this.dialCodeHolder.dom.value;
44107             }
44108             
44109             var dialCode = "";
44110             if (v.charAt(0) != "+") {
44111                 return false;
44112             }
44113             var numericChars = "";
44114             for (var i = 1; i < v.length; i++) {
44115               var c = v.charAt(i);
44116               if (!isNaN(c)) {
44117                 numericChars += c;
44118                 if (this.dialCodeMapping[numericChars]) {
44119                   dialCode = v.substr(1, i);
44120                 }
44121                 if (numericChars.length == 4) {
44122                   break;
44123                 }
44124               }
44125             }
44126             return dialCode;
44127         },
44128         
44129         reset : function()
44130         {
44131             this.setValue(this.defaultDialCode);
44132             this.validate();
44133         },
44134         
44135         hiddenEl : function()
44136         {
44137             return this.el.select('input.hidden-tel-input',true).first();
44138         },
44139         
44140         // after setting val
44141         onKeyUp : function(e){
44142             this.setValue(this.getValue());
44143         },
44144         
44145         onKeyPress : function(e){
44146             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44147                 e.stopEvent();
44148             }
44149         }
44150         
44151 });
44152 /**
44153  * @class Roo.bootstrap.MoneyField
44154  * @extends Roo.bootstrap.ComboBox
44155  * Bootstrap MoneyField class
44156  * 
44157  * @constructor
44158  * Create a new MoneyField.
44159  * @param {Object} config Configuration options
44160  */
44161
44162 Roo.bootstrap.MoneyField = function(config) {
44163     
44164     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44165     
44166 };
44167
44168 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44169     
44170     /**
44171      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44172      */
44173     allowDecimals : true,
44174     /**
44175      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44176      */
44177     decimalSeparator : ".",
44178     /**
44179      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44180      */
44181     decimalPrecision : 0,
44182     /**
44183      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44184      */
44185     allowNegative : true,
44186     /**
44187      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44188      */
44189     allowZero: true,
44190     /**
44191      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44192      */
44193     minValue : Number.NEGATIVE_INFINITY,
44194     /**
44195      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44196      */
44197     maxValue : Number.MAX_VALUE,
44198     /**
44199      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44200      */
44201     minText : "The minimum value for this field is {0}",
44202     /**
44203      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44204      */
44205     maxText : "The maximum value for this field is {0}",
44206     /**
44207      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44208      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44209      */
44210     nanText : "{0} is not a valid number",
44211     /**
44212      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44213      */
44214     castInt : true,
44215     /**
44216      * @cfg {String} defaults currency of the MoneyField
44217      * value should be in lkey
44218      */
44219     defaultCurrency : false,
44220     /**
44221      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44222      */
44223     thousandsDelimiter : false,
44224     /**
44225      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44226      */
44227     max_length: false,
44228     
44229     inputlg : 9,
44230     inputmd : 9,
44231     inputsm : 9,
44232     inputxs : 6,
44233     
44234     store : false,
44235     
44236     getAutoCreate : function()
44237     {
44238         var align = this.labelAlign || this.parentLabelAlign();
44239         
44240         var id = Roo.id();
44241
44242         var cfg = {
44243             cls: 'form-group',
44244             cn: []
44245         };
44246
44247         var input =  {
44248             tag: 'input',
44249             id : id,
44250             cls : 'form-control roo-money-amount-input',
44251             autocomplete: 'new-password'
44252         };
44253         
44254         var hiddenInput = {
44255             tag: 'input',
44256             type: 'hidden',
44257             id: Roo.id(),
44258             cls: 'hidden-number-input'
44259         };
44260         
44261         if(this.max_length) {
44262             input.maxlength = this.max_length; 
44263         }
44264         
44265         if (this.name) {
44266             hiddenInput.name = this.name;
44267         }
44268
44269         if (this.disabled) {
44270             input.disabled = true;
44271         }
44272
44273         var clg = 12 - this.inputlg;
44274         var cmd = 12 - this.inputmd;
44275         var csm = 12 - this.inputsm;
44276         var cxs = 12 - this.inputxs;
44277         
44278         var container = {
44279             tag : 'div',
44280             cls : 'row roo-money-field',
44281             cn : [
44282                 {
44283                     tag : 'div',
44284                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44285                     cn : [
44286                         {
44287                             tag : 'div',
44288                             cls: 'roo-select2-container input-group',
44289                             cn: [
44290                                 {
44291                                     tag : 'input',
44292                                     cls : 'form-control roo-money-currency-input',
44293                                     autocomplete: 'new-password',
44294                                     readOnly : 1,
44295                                     name : this.currencyName
44296                                 },
44297                                 {
44298                                     tag :'span',
44299                                     cls : 'input-group-addon',
44300                                     cn : [
44301                                         {
44302                                             tag: 'span',
44303                                             cls: 'caret'
44304                                         }
44305                                     ]
44306                                 }
44307                             ]
44308                         }
44309                     ]
44310                 },
44311                 {
44312                     tag : 'div',
44313                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44314                     cn : [
44315                         {
44316                             tag: 'div',
44317                             cls: this.hasFeedback ? 'has-feedback' : '',
44318                             cn: [
44319                                 input
44320                             ]
44321                         }
44322                     ]
44323                 }
44324             ]
44325             
44326         };
44327         
44328         if (this.fieldLabel.length) {
44329             var indicator = {
44330                 tag: 'i',
44331                 tooltip: 'This field is required'
44332             };
44333
44334             var label = {
44335                 tag: 'label',
44336                 'for':  id,
44337                 cls: 'control-label',
44338                 cn: []
44339             };
44340
44341             var label_text = {
44342                 tag: 'span',
44343                 html: this.fieldLabel
44344             };
44345
44346             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44347             label.cn = [
44348                 indicator,
44349                 label_text
44350             ];
44351
44352             if(this.indicatorpos == 'right') {
44353                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44354                 label.cn = [
44355                     label_text,
44356                     indicator
44357                 ];
44358             }
44359
44360             if(align == 'left') {
44361                 container = {
44362                     tag: 'div',
44363                     cn: [
44364                         container
44365                     ]
44366                 };
44367
44368                 if(this.labelWidth > 12){
44369                     label.style = "width: " + this.labelWidth + 'px';
44370                 }
44371                 if(this.labelWidth < 13 && this.labelmd == 0){
44372                     this.labelmd = this.labelWidth;
44373                 }
44374                 if(this.labellg > 0){
44375                     label.cls += ' col-lg-' + this.labellg;
44376                     input.cls += ' col-lg-' + (12 - this.labellg);
44377                 }
44378                 if(this.labelmd > 0){
44379                     label.cls += ' col-md-' + this.labelmd;
44380                     container.cls += ' col-md-' + (12 - this.labelmd);
44381                 }
44382                 if(this.labelsm > 0){
44383                     label.cls += ' col-sm-' + this.labelsm;
44384                     container.cls += ' col-sm-' + (12 - this.labelsm);
44385                 }
44386                 if(this.labelxs > 0){
44387                     label.cls += ' col-xs-' + this.labelxs;
44388                     container.cls += ' col-xs-' + (12 - this.labelxs);
44389                 }
44390             }
44391         }
44392
44393         cfg.cn = [
44394             label,
44395             container,
44396             hiddenInput
44397         ];
44398         
44399         var settings = this;
44400
44401         ['xs','sm','md','lg'].map(function(size){
44402             if (settings[size]) {
44403                 cfg.cls += ' col-' + size + '-' + settings[size];
44404             }
44405         });
44406         
44407         return cfg;
44408     },
44409     
44410     initEvents : function()
44411     {
44412         this.indicator = this.indicatorEl();
44413         
44414         this.initCurrencyEvent();
44415         
44416         this.initNumberEvent();
44417     },
44418     
44419     initCurrencyEvent : function()
44420     {
44421         if (!this.store) {
44422             throw "can not find store for combo";
44423         }
44424         
44425         this.store = Roo.factory(this.store, Roo.data);
44426         this.store.parent = this;
44427         
44428         this.createList();
44429         
44430         this.triggerEl = this.el.select('.input-group-addon', true).first();
44431         
44432         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44433         
44434         var _this = this;
44435         
44436         (function(){
44437             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44438             _this.list.setWidth(lw);
44439         }).defer(100);
44440         
44441         this.list.on('mouseover', this.onViewOver, this);
44442         this.list.on('mousemove', this.onViewMove, this);
44443         this.list.on('scroll', this.onViewScroll, this);
44444         
44445         if(!this.tpl){
44446             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44447         }
44448         
44449         this.view = new Roo.View(this.list, this.tpl, {
44450             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44451         });
44452         
44453         this.view.on('click', this.onViewClick, this);
44454         
44455         this.store.on('beforeload', this.onBeforeLoad, this);
44456         this.store.on('load', this.onLoad, this);
44457         this.store.on('loadexception', this.onLoadException, this);
44458         
44459         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44460             "up" : function(e){
44461                 this.inKeyMode = true;
44462                 this.selectPrev();
44463             },
44464
44465             "down" : function(e){
44466                 if(!this.isExpanded()){
44467                     this.onTriggerClick();
44468                 }else{
44469                     this.inKeyMode = true;
44470                     this.selectNext();
44471                 }
44472             },
44473
44474             "enter" : function(e){
44475                 this.collapse();
44476                 
44477                 if(this.fireEvent("specialkey", this, e)){
44478                     this.onViewClick(false);
44479                 }
44480                 
44481                 return true;
44482             },
44483
44484             "esc" : function(e){
44485                 this.collapse();
44486             },
44487
44488             "tab" : function(e){
44489                 this.collapse();
44490                 
44491                 if(this.fireEvent("specialkey", this, e)){
44492                     this.onViewClick(false);
44493                 }
44494                 
44495                 return true;
44496             },
44497
44498             scope : this,
44499
44500             doRelay : function(foo, bar, hname){
44501                 if(hname == 'down' || this.scope.isExpanded()){
44502                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44503                 }
44504                 return true;
44505             },
44506
44507             forceKeyDown: true
44508         });
44509         
44510         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44511         
44512     },
44513     
44514     initNumberEvent : function(e)
44515     {
44516         this.inputEl().on("keydown" , this.fireKey,  this);
44517         this.inputEl().on("focus", this.onFocus,  this);
44518         this.inputEl().on("blur", this.onBlur,  this);
44519         
44520         this.inputEl().relayEvent('keyup', this);
44521         
44522         if(this.indicator){
44523             this.indicator.addClass('invisible');
44524         }
44525  
44526         this.originalValue = this.getValue();
44527         
44528         if(this.validationEvent == 'keyup'){
44529             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44530             this.inputEl().on('keyup', this.filterValidation, this);
44531         }
44532         else if(this.validationEvent !== false){
44533             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44534         }
44535         
44536         if(this.selectOnFocus){
44537             this.on("focus", this.preFocus, this);
44538             
44539         }
44540         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44541             this.inputEl().on("keypress", this.filterKeys, this);
44542         } else {
44543             this.inputEl().relayEvent('keypress', this);
44544         }
44545         
44546         var allowed = "0123456789";
44547         
44548         if(this.allowDecimals){
44549             allowed += this.decimalSeparator;
44550         }
44551         
44552         if(this.allowNegative){
44553             allowed += "-";
44554         }
44555         
44556         if(this.thousandsDelimiter) {
44557             allowed += ",";
44558         }
44559         
44560         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44561         
44562         var keyPress = function(e){
44563             
44564             var k = e.getKey();
44565             
44566             var c = e.getCharCode();
44567             
44568             if(
44569                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44570                     allowed.indexOf(String.fromCharCode(c)) === -1
44571             ){
44572                 e.stopEvent();
44573                 return;
44574             }
44575             
44576             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44577                 return;
44578             }
44579             
44580             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44581                 e.stopEvent();
44582             }
44583         };
44584         
44585         this.inputEl().on("keypress", keyPress, this);
44586         
44587     },
44588     
44589     onTriggerClick : function(e)
44590     {   
44591         if(this.disabled){
44592             return;
44593         }
44594         
44595         this.page = 0;
44596         this.loadNext = false;
44597         
44598         if(this.isExpanded()){
44599             this.collapse();
44600             return;
44601         }
44602         
44603         this.hasFocus = true;
44604         
44605         if(this.triggerAction == 'all') {
44606             this.doQuery(this.allQuery, true);
44607             return;
44608         }
44609         
44610         this.doQuery(this.getRawValue());
44611     },
44612     
44613     getCurrency : function()
44614     {   
44615         var v = this.currencyEl().getValue();
44616         
44617         return v;
44618     },
44619     
44620     restrictHeight : function()
44621     {
44622         this.list.alignTo(this.currencyEl(), this.listAlign);
44623         this.list.alignTo(this.currencyEl(), this.listAlign);
44624     },
44625     
44626     onViewClick : function(view, doFocus, el, e)
44627     {
44628         var index = this.view.getSelectedIndexes()[0];
44629         
44630         var r = this.store.getAt(index);
44631         
44632         if(r){
44633             this.onSelect(r, index);
44634         }
44635     },
44636     
44637     onSelect : function(record, index){
44638         
44639         if(this.fireEvent('beforeselect', this, record, index) !== false){
44640         
44641             this.setFromCurrencyData(index > -1 ? record.data : false);
44642             
44643             this.collapse();
44644             
44645             this.fireEvent('select', this, record, index);
44646         }
44647     },
44648     
44649     setFromCurrencyData : function(o)
44650     {
44651         var currency = '';
44652         
44653         this.lastCurrency = o;
44654         
44655         if (this.currencyField) {
44656             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44657         } else {
44658             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44659         }
44660         
44661         this.lastSelectionText = currency;
44662         
44663         //setting default currency
44664         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44665             this.setCurrency(this.defaultCurrency);
44666             return;
44667         }
44668         
44669         this.setCurrency(currency);
44670     },
44671     
44672     setFromData : function(o)
44673     {
44674         var c = {};
44675         
44676         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44677         
44678         this.setFromCurrencyData(c);
44679         
44680         var value = '';
44681         
44682         if (this.name) {
44683             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44684         } else {
44685             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44686         }
44687         
44688         this.setValue(value);
44689         
44690     },
44691     
44692     setCurrency : function(v)
44693     {   
44694         this.currencyValue = v;
44695         
44696         if(this.rendered){
44697             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44698             this.validate();
44699         }
44700     },
44701     
44702     setValue : function(v)
44703     {
44704         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44705         
44706         this.value = v;
44707         
44708         if(this.rendered){
44709             
44710             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44711             
44712             this.inputEl().dom.value = (v == '') ? '' :
44713                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44714             
44715             if(!this.allowZero && v === '0') {
44716                 this.hiddenEl().dom.value = '';
44717                 this.inputEl().dom.value = '';
44718             }
44719             
44720             this.validate();
44721         }
44722     },
44723     
44724     getRawValue : function()
44725     {
44726         var v = this.inputEl().getValue();
44727         
44728         return v;
44729     },
44730     
44731     getValue : function()
44732     {
44733         return this.fixPrecision(this.parseValue(this.getRawValue()));
44734     },
44735     
44736     parseValue : function(value)
44737     {
44738         if(this.thousandsDelimiter) {
44739             value += "";
44740             r = new RegExp(",", "g");
44741             value = value.replace(r, "");
44742         }
44743         
44744         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44745         return isNaN(value) ? '' : value;
44746         
44747     },
44748     
44749     fixPrecision : function(value)
44750     {
44751         if(this.thousandsDelimiter) {
44752             value += "";
44753             r = new RegExp(",", "g");
44754             value = value.replace(r, "");
44755         }
44756         
44757         var nan = isNaN(value);
44758         
44759         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44760             return nan ? '' : value;
44761         }
44762         return parseFloat(value).toFixed(this.decimalPrecision);
44763     },
44764     
44765     decimalPrecisionFcn : function(v)
44766     {
44767         return Math.floor(v);
44768     },
44769     
44770     validateValue : function(value)
44771     {
44772         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44773             return false;
44774         }
44775         
44776         var num = this.parseValue(value);
44777         
44778         if(isNaN(num)){
44779             this.markInvalid(String.format(this.nanText, value));
44780             return false;
44781         }
44782         
44783         if(num < this.minValue){
44784             this.markInvalid(String.format(this.minText, this.minValue));
44785             return false;
44786         }
44787         
44788         if(num > this.maxValue){
44789             this.markInvalid(String.format(this.maxText, this.maxValue));
44790             return false;
44791         }
44792         
44793         return true;
44794     },
44795     
44796     validate : function()
44797     {
44798         if(this.disabled || this.allowBlank){
44799             this.markValid();
44800             return true;
44801         }
44802         
44803         var currency = this.getCurrency();
44804         
44805         if(this.validateValue(this.getRawValue()) && currency.length){
44806             this.markValid();
44807             return true;
44808         }
44809         
44810         this.markInvalid();
44811         return false;
44812     },
44813     
44814     getName: function()
44815     {
44816         return this.name;
44817     },
44818     
44819     beforeBlur : function()
44820     {
44821         if(!this.castInt){
44822             return;
44823         }
44824         
44825         var v = this.parseValue(this.getRawValue());
44826         
44827         if(v || v == 0){
44828             this.setValue(v);
44829         }
44830     },
44831     
44832     onBlur : function()
44833     {
44834         this.beforeBlur();
44835         
44836         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44837             //this.el.removeClass(this.focusClass);
44838         }
44839         
44840         this.hasFocus = false;
44841         
44842         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44843             this.validate();
44844         }
44845         
44846         var v = this.getValue();
44847         
44848         if(String(v) !== String(this.startValue)){
44849             this.fireEvent('change', this, v, this.startValue);
44850         }
44851         
44852         this.fireEvent("blur", this);
44853     },
44854     
44855     inputEl : function()
44856     {
44857         return this.el.select('.roo-money-amount-input', true).first();
44858     },
44859     
44860     currencyEl : function()
44861     {
44862         return this.el.select('.roo-money-currency-input', true).first();
44863     },
44864     
44865     hiddenEl : function()
44866     {
44867         return this.el.select('input.hidden-number-input',true).first();
44868     }
44869     
44870 });/**
44871  * @class Roo.bootstrap.BezierSignature
44872  * @extends Roo.bootstrap.Component
44873  * Bootstrap BezierSignature class
44874  * This script refer to:
44875  *    Title: Signature Pad
44876  *    Author: szimek
44877  *    Availability: https://github.com/szimek/signature_pad
44878  *
44879  * @constructor
44880  * Create a new BezierSignature
44881  * @param {Object} config The config object
44882  */
44883
44884 Roo.bootstrap.BezierSignature = function(config){
44885     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44886     this.addEvents({
44887         "resize" : true
44888     });
44889 };
44890
44891 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44892 {
44893      
44894     curve_data: [],
44895     
44896     is_empty: true,
44897     
44898     mouse_btn_down: true,
44899     
44900     /**
44901      * @cfg {int} canvas height
44902      */
44903     canvas_height: '200px',
44904     
44905     /**
44906      * @cfg {float|function} Radius of a single dot.
44907      */ 
44908     dot_size: false,
44909     
44910     /**
44911      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44912      */
44913     min_width: 0.5,
44914     
44915     /**
44916      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44917      */
44918     max_width: 2.5,
44919     
44920     /**
44921      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44922      */
44923     throttle: 16,
44924     
44925     /**
44926      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44927      */
44928     min_distance: 5,
44929     
44930     /**
44931      * @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.
44932      */
44933     bg_color: 'rgba(0, 0, 0, 0)',
44934     
44935     /**
44936      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44937      */
44938     dot_color: 'black',
44939     
44940     /**
44941      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44942      */ 
44943     velocity_filter_weight: 0.7,
44944     
44945     /**
44946      * @cfg {function} Callback when stroke begin. 
44947      */
44948     onBegin: false,
44949     
44950     /**
44951      * @cfg {function} Callback when stroke end.
44952      */
44953     onEnd: false,
44954     
44955     getAutoCreate : function()
44956     {
44957         var cls = 'roo-signature column';
44958         
44959         if(this.cls){
44960             cls += ' ' + this.cls;
44961         }
44962         
44963         var col_sizes = [
44964             'lg',
44965             'md',
44966             'sm',
44967             'xs'
44968         ];
44969         
44970         for(var i = 0; i < col_sizes.length; i++) {
44971             if(this[col_sizes[i]]) {
44972                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44973             }
44974         }
44975         
44976         var cfg = {
44977             tag: 'div',
44978             cls: cls,
44979             cn: [
44980                 {
44981                     tag: 'div',
44982                     cls: 'roo-signature-body',
44983                     cn: [
44984                         {
44985                             tag: 'canvas',
44986                             cls: 'roo-signature-body-canvas',
44987                             height: this.canvas_height,
44988                             width: this.canvas_width
44989                         }
44990                     ]
44991                 },
44992                 {
44993                     tag: 'input',
44994                     type: 'file',
44995                     style: 'display: none'
44996                 }
44997             ]
44998         };
44999         
45000         return cfg;
45001     },
45002     
45003     initEvents: function() 
45004     {
45005         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45006         
45007         var canvas = this.canvasEl();
45008         
45009         // mouse && touch event swapping...
45010         canvas.dom.style.touchAction = 'none';
45011         canvas.dom.style.msTouchAction = 'none';
45012         
45013         this.mouse_btn_down = false;
45014         canvas.on('mousedown', this._handleMouseDown, this);
45015         canvas.on('mousemove', this._handleMouseMove, this);
45016         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45017         
45018         if (window.PointerEvent) {
45019             canvas.on('pointerdown', this._handleMouseDown, this);
45020             canvas.on('pointermove', this._handleMouseMove, this);
45021             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45022         }
45023         
45024         if ('ontouchstart' in window) {
45025             canvas.on('touchstart', this._handleTouchStart, this);
45026             canvas.on('touchmove', this._handleTouchMove, this);
45027             canvas.on('touchend', this._handleTouchEnd, this);
45028         }
45029         
45030         Roo.EventManager.onWindowResize(this.resize, this, true);
45031         
45032         // file input event
45033         this.fileEl().on('change', this.uploadImage, this);
45034         
45035         this.clear();
45036         
45037         this.resize();
45038     },
45039     
45040     resize: function(){
45041         
45042         var canvas = this.canvasEl().dom;
45043         var ctx = this.canvasElCtx();
45044         var img_data = false;
45045         
45046         if(canvas.width > 0) {
45047             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45048         }
45049         // setting canvas width will clean img data
45050         canvas.width = 0;
45051         
45052         var style = window.getComputedStyle ? 
45053             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45054             
45055         var padding_left = parseInt(style.paddingLeft) || 0;
45056         var padding_right = parseInt(style.paddingRight) || 0;
45057         
45058         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45059         
45060         if(img_data) {
45061             ctx.putImageData(img_data, 0, 0);
45062         }
45063     },
45064     
45065     _handleMouseDown: function(e)
45066     {
45067         if (e.browserEvent.which === 1) {
45068             this.mouse_btn_down = true;
45069             this.strokeBegin(e);
45070         }
45071     },
45072     
45073     _handleMouseMove: function (e)
45074     {
45075         if (this.mouse_btn_down) {
45076             this.strokeMoveUpdate(e);
45077         }
45078     },
45079     
45080     _handleMouseUp: function (e)
45081     {
45082         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45083             this.mouse_btn_down = false;
45084             this.strokeEnd(e);
45085         }
45086     },
45087     
45088     _handleTouchStart: function (e) {
45089         
45090         e.preventDefault();
45091         if (e.browserEvent.targetTouches.length === 1) {
45092             // var touch = e.browserEvent.changedTouches[0];
45093             // this.strokeBegin(touch);
45094             
45095              this.strokeBegin(e); // assume e catching the correct xy...
45096         }
45097     },
45098     
45099     _handleTouchMove: function (e) {
45100         e.preventDefault();
45101         // var touch = event.targetTouches[0];
45102         // _this._strokeMoveUpdate(touch);
45103         this.strokeMoveUpdate(e);
45104     },
45105     
45106     _handleTouchEnd: function (e) {
45107         var wasCanvasTouched = e.target === this.canvasEl().dom;
45108         if (wasCanvasTouched) {
45109             e.preventDefault();
45110             // var touch = event.changedTouches[0];
45111             // _this._strokeEnd(touch);
45112             this.strokeEnd(e);
45113         }
45114     },
45115     
45116     reset: function () {
45117         this._lastPoints = [];
45118         this._lastVelocity = 0;
45119         this._lastWidth = (this.min_width + this.max_width) / 2;
45120         this.canvasElCtx().fillStyle = this.dot_color;
45121     },
45122     
45123     strokeMoveUpdate: function(e)
45124     {
45125         this.strokeUpdate(e);
45126         
45127         if (this.throttle) {
45128             this.throttleStroke(this.strokeUpdate, this.throttle);
45129         }
45130         else {
45131             this.strokeUpdate(e);
45132         }
45133     },
45134     
45135     strokeBegin: function(e)
45136     {
45137         var newPointGroup = {
45138             color: this.dot_color,
45139             points: []
45140         };
45141         
45142         if (typeof this.onBegin === 'function') {
45143             this.onBegin(e);
45144         }
45145         
45146         this.curve_data.push(newPointGroup);
45147         this.reset();
45148         this.strokeUpdate(e);
45149     },
45150     
45151     strokeUpdate: function(e)
45152     {
45153         var rect = this.canvasEl().dom.getBoundingClientRect();
45154         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45155         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45156         var lastPoints = lastPointGroup.points;
45157         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45158         var isLastPointTooClose = lastPoint
45159             ? point.distanceTo(lastPoint) <= this.min_distance
45160             : false;
45161         var color = lastPointGroup.color;
45162         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45163             var curve = this.addPoint(point);
45164             if (!lastPoint) {
45165                 this.drawDot({color: color, point: point});
45166             }
45167             else if (curve) {
45168                 this.drawCurve({color: color, curve: curve});
45169             }
45170             lastPoints.push({
45171                 time: point.time,
45172                 x: point.x,
45173                 y: point.y
45174             });
45175         }
45176     },
45177     
45178     strokeEnd: function(e)
45179     {
45180         this.strokeUpdate(e);
45181         if (typeof this.onEnd === 'function') {
45182             this.onEnd(e);
45183         }
45184     },
45185     
45186     addPoint:  function (point) {
45187         var _lastPoints = this._lastPoints;
45188         _lastPoints.push(point);
45189         if (_lastPoints.length > 2) {
45190             if (_lastPoints.length === 3) {
45191                 _lastPoints.unshift(_lastPoints[0]);
45192             }
45193             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45194             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45195             _lastPoints.shift();
45196             return curve;
45197         }
45198         return null;
45199     },
45200     
45201     calculateCurveWidths: function (startPoint, endPoint) {
45202         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45203             (1 - this.velocity_filter_weight) * this._lastVelocity;
45204
45205         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45206         var widths = {
45207             end: newWidth,
45208             start: this._lastWidth
45209         };
45210         
45211         this._lastVelocity = velocity;
45212         this._lastWidth = newWidth;
45213         return widths;
45214     },
45215     
45216     drawDot: function (_a) {
45217         var color = _a.color, point = _a.point;
45218         var ctx = this.canvasElCtx();
45219         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45220         ctx.beginPath();
45221         this.drawCurveSegment(point.x, point.y, width);
45222         ctx.closePath();
45223         ctx.fillStyle = color;
45224         ctx.fill();
45225     },
45226     
45227     drawCurve: function (_a) {
45228         var color = _a.color, curve = _a.curve;
45229         var ctx = this.canvasElCtx();
45230         var widthDelta = curve.endWidth - curve.startWidth;
45231         var drawSteps = Math.floor(curve.length()) * 2;
45232         ctx.beginPath();
45233         ctx.fillStyle = color;
45234         for (var i = 0; i < drawSteps; i += 1) {
45235         var t = i / drawSteps;
45236         var tt = t * t;
45237         var ttt = tt * t;
45238         var u = 1 - t;
45239         var uu = u * u;
45240         var uuu = uu * u;
45241         var x = uuu * curve.startPoint.x;
45242         x += 3 * uu * t * curve.control1.x;
45243         x += 3 * u * tt * curve.control2.x;
45244         x += ttt * curve.endPoint.x;
45245         var y = uuu * curve.startPoint.y;
45246         y += 3 * uu * t * curve.control1.y;
45247         y += 3 * u * tt * curve.control2.y;
45248         y += ttt * curve.endPoint.y;
45249         var width = curve.startWidth + ttt * widthDelta;
45250         this.drawCurveSegment(x, y, width);
45251         }
45252         ctx.closePath();
45253         ctx.fill();
45254     },
45255     
45256     drawCurveSegment: function (x, y, width) {
45257         var ctx = this.canvasElCtx();
45258         ctx.moveTo(x, y);
45259         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45260         this.is_empty = false;
45261     },
45262     
45263     clear: function()
45264     {
45265         var ctx = this.canvasElCtx();
45266         var canvas = this.canvasEl().dom;
45267         ctx.fillStyle = this.bg_color;
45268         ctx.clearRect(0, 0, canvas.width, canvas.height);
45269         ctx.fillRect(0, 0, canvas.width, canvas.height);
45270         this.curve_data = [];
45271         this.reset();
45272         this.is_empty = true;
45273     },
45274     
45275     fileEl: function()
45276     {
45277         return  this.el.select('input',true).first();
45278     },
45279     
45280     canvasEl: function()
45281     {
45282         return this.el.select('canvas',true).first();
45283     },
45284     
45285     canvasElCtx: function()
45286     {
45287         return this.el.select('canvas',true).first().dom.getContext('2d');
45288     },
45289     
45290     getImage: function(type)
45291     {
45292         if(this.is_empty) {
45293             return false;
45294         }
45295         
45296         // encryption ?
45297         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45298     },
45299     
45300     drawFromImage: function(img_src)
45301     {
45302         var img = new Image();
45303         
45304         img.onload = function(){
45305             this.canvasElCtx().drawImage(img, 0, 0);
45306         }.bind(this);
45307         
45308         img.src = img_src;
45309         
45310         this.is_empty = false;
45311     },
45312     
45313     selectImage: function()
45314     {
45315         this.fileEl().dom.click();
45316     },
45317     
45318     uploadImage: function(e)
45319     {
45320         var reader = new FileReader();
45321         
45322         reader.onload = function(e){
45323             var img = new Image();
45324             img.onload = function(){
45325                 this.reset();
45326                 this.canvasElCtx().drawImage(img, 0, 0);
45327             }.bind(this);
45328             img.src = e.target.result;
45329         }.bind(this);
45330         
45331         reader.readAsDataURL(e.target.files[0]);
45332     },
45333     
45334     // Bezier Point Constructor
45335     Point: (function () {
45336         function Point(x, y, time) {
45337             this.x = x;
45338             this.y = y;
45339             this.time = time || Date.now();
45340         }
45341         Point.prototype.distanceTo = function (start) {
45342             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45343         };
45344         Point.prototype.equals = function (other) {
45345             return this.x === other.x && this.y === other.y && this.time === other.time;
45346         };
45347         Point.prototype.velocityFrom = function (start) {
45348             return this.time !== start.time
45349             ? this.distanceTo(start) / (this.time - start.time)
45350             : 0;
45351         };
45352         return Point;
45353     }()),
45354     
45355     
45356     // Bezier Constructor
45357     Bezier: (function () {
45358         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45359             this.startPoint = startPoint;
45360             this.control2 = control2;
45361             this.control1 = control1;
45362             this.endPoint = endPoint;
45363             this.startWidth = startWidth;
45364             this.endWidth = endWidth;
45365         }
45366         Bezier.fromPoints = function (points, widths, scope) {
45367             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45368             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45369             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45370         };
45371         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45372             var dx1 = s1.x - s2.x;
45373             var dy1 = s1.y - s2.y;
45374             var dx2 = s2.x - s3.x;
45375             var dy2 = s2.y - s3.y;
45376             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45377             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45378             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45379             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45380             var dxm = m1.x - m2.x;
45381             var dym = m1.y - m2.y;
45382             var k = l2 / (l1 + l2);
45383             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45384             var tx = s2.x - cm.x;
45385             var ty = s2.y - cm.y;
45386             return {
45387                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45388                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45389             };
45390         };
45391         Bezier.prototype.length = function () {
45392             var steps = 10;
45393             var length = 0;
45394             var px;
45395             var py;
45396             for (var i = 0; i <= steps; i += 1) {
45397                 var t = i / steps;
45398                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45399                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45400                 if (i > 0) {
45401                     var xdiff = cx - px;
45402                     var ydiff = cy - py;
45403                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45404                 }
45405                 px = cx;
45406                 py = cy;
45407             }
45408             return length;
45409         };
45410         Bezier.prototype.point = function (t, start, c1, c2, end) {
45411             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45412             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45413             + (3.0 * c2 * (1.0 - t) * t * t)
45414             + (end * t * t * t);
45415         };
45416         return Bezier;
45417     }()),
45418     
45419     throttleStroke: function(fn, wait) {
45420       if (wait === void 0) { wait = 250; }
45421       var previous = 0;
45422       var timeout = null;
45423       var result;
45424       var storedContext;
45425       var storedArgs;
45426       var later = function () {
45427           previous = Date.now();
45428           timeout = null;
45429           result = fn.apply(storedContext, storedArgs);
45430           if (!timeout) {
45431               storedContext = null;
45432               storedArgs = [];
45433           }
45434       };
45435       return function wrapper() {
45436           var args = [];
45437           for (var _i = 0; _i < arguments.length; _i++) {
45438               args[_i] = arguments[_i];
45439           }
45440           var now = Date.now();
45441           var remaining = wait - (now - previous);
45442           storedContext = this;
45443           storedArgs = args;
45444           if (remaining <= 0 || remaining > wait) {
45445               if (timeout) {
45446                   clearTimeout(timeout);
45447                   timeout = null;
45448               }
45449               previous = now;
45450               result = fn.apply(storedContext, storedArgs);
45451               if (!timeout) {
45452                   storedContext = null;
45453                   storedArgs = [];
45454               }
45455           }
45456           else if (!timeout) {
45457               timeout = window.setTimeout(later, remaining);
45458           }
45459           return result;
45460       };
45461   }
45462   
45463 });
45464
45465  
45466
45467